PHP restrict calling of public methods [closed]

2019-06-06 11:31发布

问题:

I'm working on a library that has quite a few classes that are all pulled together by a central class. This central class has to call certain methods on the other classes for set-up/configuration purposes. These methods have to be public so that the central class can call them, but I do not want users to call these methods (as it may cause undesired behavior).

One suggestion a friend of mine made was to use a constructor with parameters can have the set-up done at construction time. For the purposes of this library, that is not easily possible. The classes in question are intended to be extended, and I didn't want to impose any weird requirements on constructor parameters if the users want to have their own constructors. Even more complicated is that some of the information used for configuration isn't available to the user. I would have to make it available to the user and trust they will route it back to the proper classes during construction.

Currently, my solution is to prefix these methods with something and note to users not to call methods with said prefix. This is "flexible" enough to let me add more restricted methods, but it still makes the assumption that the user will follow my instructions. It's also not the most elegant of solutions.

I was wondering if there was some way to restrict these methods easily. I thought about adding a line in them that checked if it was the central class calling them, but that doesn't seem like the best solution either.

Edit: I should explain the purpose of this architecture. The classes each perform a particular task in a stack of operations. The job of the central class is to command one class to perform it's task, gather results, and disperse the results to the other classes that need them. Then the class moves to the next class and does the same thing. How the tasks are performed is up to the extended class. The methods I want restricted are the ones that aid in the dispersing of the results. I hope that makes my intentions more clear.

回答1:

It all depends on the role of your central class. From your description it seems to be a spooler or scheduler of sorts. If the spooled objects are actually created by that central class, you can hide the objects from the client by making those objects private, and then create stubs in the central object that allows the client to call the functions that you do allow.

E.g. (conceptual code, might not actually compile)

class HiddenSubClass {
  public function MyFunc() {
    echo "hello";
  }
}

class CentralClass {
  private $obj;

  function __construct() {
     $obj=new HiddenSubClass();
  }

  function SubClassMyFunc () {
    $obj->MyFunc();
  }
}

The client only has the CentralClass object and hence can only call SubClassMyFunc, but not MyFunc. The downside of this is that this will also hide the object oriented structure of the library from the client, which may or may not be desireable.

If, however, the objects are created by the client and then handed over to your central class, there is very little you can do to prevent the owner of the object (the client) do with it whatever it wants. The client can call anything your central class can. You might then have to move the code you want to protect out of said class - either into "helper classes" or into the central class itself.

Edit: If you do want to "hack" it, this would be one approach (although I don't recommend it, because it's quite smelly):

Have a "private secret" in your central class.

class CentralClass {
  private $MySecret=42;

  function IsValidSecret($anumber) {
    return ($anumber==$this->MySecret);   
  } 
}

Now the central class can pass $MySecret as parameter, when it calls certain "protected" function on the spooled objects. If CentralClass is a singleton, the spooled object can call that singleton to check if the passed secret is correct. If CentralClass is not a singleton, you'll have to pass that object as well.

class MySpooledObject {
  function SuperSecretFunction ($asecret) {
    if (!$CentralSingleton->isValidSecret($asecret)) { die(); }
  }

  function SuperSecretFunction2 ($acentralobject,$asecret) {
    if (!$acentralobject->isValidSecret($asecret)) { die(); }
  }
}

There are many variations of this. The central class could set an internal flag before calling the object's method, and reset that upon its return. In this scenario the object can ask the central class if that flag is set, and no secret needs to be passed. You'll still need the $acentralobject however, unless it is a singleton.