views:

70

answers:

2

Hi all I'm wondering if it's possible to encapsulate the methods of a class, but then expose them within a consuming class. For example (JFTR, I know this code is wrong)

class Consumer{
        public function __construct($obj){
            $this->obj = $obj;
            }

        public function doCommand(){
            $this->obj->command();
            }
        }

     class Consumed{
         //I would make the constructor private, but to save space...
         public function __construct(){}
         private function command(){
             echo "Executing command in the context of the Consumer";
             }
         }

     $consumer = new Consumer(new Consumed);
     $consumer->doCommand();

     //just to reiterate, I know this throws an error

Ultimately, I want to be able to make components that cannot be directly referenced outside the context of a single controlling class.

+1  A: 

You could emulate something similar with __call and debug_backtrace.

<?php
class Consumer{
  public function __construct($obj){
    $this->obj = $obj;
  }

  public function doCommand(){
    $this->obj->command();
  }
}

class Consumed {
  // classes that are aloowed to call private functions
  private $friends = array('Consumer');

  public function __construct(){}
  private function command() {
    echo "Executing command in the context of the Consumer. \n";
  }

  public function __call($name, $arguments) {
    $dt = debug_backtrace();
    // [0] describes this method's frame
    // [1] is the would-be frame of the method command()
    // [2] is the frame of the direct caller. That's the interesting one.
    if ( isset($dt[2], $dt[2]['class']) && in_array($dt[2]['class'], $this->friends) ) {
      return call_user_func_array(array($this,$name), $arguments);
    }
    else {
      die('!__call()');
    }
  }
}

$c = new Consumed;
$consumer = new Consumer($c);
$consumer->doCommand();

echo 'and now without Consumer: '; 
$c->command();

prints

Executing command in the context of the Consumer. 
and now without Consumer: !__call()
VolkerK
A: 

Sure it can, just make those methods protected, not private and have Consumed extend from Consumer. I'm not sure about the benefits though.

Ionuț G. Stan
to prevent direct access to their methods, and force a single point of entry. Is this bad from a design perspective?
sunwukung
It may be. I mean, that `Consumed` class is basically untestable. What would be the purpose of a class with no public interface? Maybe that's a sign you don't really need that class. Access restrictions don't necessarily guarantee a good design. What is the **problem** that you are trying to solve?
Ionuț G. Stan
none really - I'm just studying the subject. I'm rolling my own MVC as part of that (redundant, I know, but an intellectual exercise to help me learn) process. In my presentation layer, there is a class of object that is used by the view controller to serve partial templates into a master- and I want to prevent that object from getting accessed directly.I thought about it after looking at the strategy/command pattern - in the examples I've seen, the consumed components could just be accessed directly - and it seemed that the single point of entry was just honoured as a point of principle.
sunwukung
It's fine to roll your own MVC, you're going to learn a lot. For the other part though, I'm afraid I don't really understand the overall wiring of objects so that I can give any opinions. In my experience I've noticed that worrying about access control has the effect of low productivity. I assume you want to learn about design patterns and stuff, but try to assume your library's virtual users are going to be mature enough to not shot themselves in the feet. Create a good, extensible design and nobody will want to access what should be protected. If they do they're responsible for their actions
Ionuț G. Stan
good advice, thanks for the help
sunwukung