While creating a PHP helper method, I am trying to find an elegant way to have the same method available for both static calls and instanciated calls, while still being able to access the instance when called from the instance call.
Consider getModelName() method within an Entity class:
public static function getModelName($entity) {
if (is_string($entity)) {
$entity = ///query to return an entity object;
return !empty($entity) ? $entity->model : false;
}
if (is_a($entity, "\namespace\path\Entity")) {
return $entity->model;
}
return false;
}
Since different classes across the application use this method, and some of them already have an instance of $entity, I want to be able to call it directly so I don't need to pass an instance of $entity as an argument every time:
$entity->getModelName();
The method itself need to be able to do:
public static function getModelName($entity = null) {
if (is_string($entity)) {
$entity = ///query to return an entity object;
return !empty($entity) ? $entity->model : false;
}
if (empty($entity)) {
//this is where it fails of course since $this is not available
//in a static method
return $this->model;
}
return false;
}
This solution obviously fails since $this is not available within a static method.
Any ideas how to solve this predicament without having 2 different methods?
Thanks for the help!
UPDATE
I am using Laravel so $entity is a database ORM model (Eloquent). I didn't think it's relevant originally because this is a general question about PHP method scopes. After reading the comments I see it's important to mention. Instanciaing a new $entity is only an option if I have to since it will query the database.
Since there's no way to access $this inside static methods, you should look at this problem from a different angle.
Simply put, $this is an instance of the current class, therefore you need to instantiate it and then access its properties or methods. So you can handle it like this:
if (empty($entity)) {
$instance = new self();
return $instance->model;
}
Related
In my current application I have a number of objects that are required repeatedly
To save overhead of instantiating the same object over and over again, I keep an array of 'known' objects.
I can check the input data against the array, and - if already set - use the existing object, else go ahead to instantiate the new object and add the pointer reference to the relevant known objects array.
In most use cases, I can check prior to instantiating the class:
if(array_key_exists($identifier,$known_ClassObjects)){
$object = $known_ClassObjects[$identifier];
} else {
$object = new Class($params);
}
However, in some cases I can only identify that the object I am instantiating already exists once already inside it.
In that case I would like to be able to do one of 2 things:
return the OTHER (pre-existing) object instead of this one, e.g.
class Test{
public function __construct($params){
//apply processing to $params, resulting in $identifier
if(array_key_exists($identifier, $known_ClassObjects)){ //$known_ClassObjects is global
return $known_ClassObjects[$identifier];
} else {
//continue __construct() logic
return $this;
}
}
}
However, PHP ALWAYS returns the current object, even with code return $other_object;
'Internally Clone' the current object from the found one [of the same class, obv] so that when it returns, it has the correct relevant properties populated.
NOTE: including any parent/child class properties
-> I want to make this object EXACTLY the same as the found one.
So, if there was a PHP function clone_from(), it would work something like:
if(array_key_exists($identifier,$known_ClassObjects)){
$this->clone_from ($known_ClassObjects[$identifier]);
} else {
//continue with __construct()
}
return $this;
Given that 1. doesn't work and 2. doesn't seem to exist, I have only been able to do this in very 'hacky' ways: iterating through all properties of source object and setting all properties of current object.
However, this has obvious issues esp. with extended parent/child classes etc. which then requires things like reflection classes.
This seems like there SHOULD be a really simple solution, but I have been unable to find one
What you actually could do is using either a Singleton Pattern or a Factory pattern - in both cases, the creation of objects is controlled by some piece of code and you can decide, which object to return. Singleton already is a special form of a Factory pattern.
Consider this code
class Singleton {
protected static $instance;
protected function __construct() {
}
public static function instance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
The constructor is protected which will prevent a object construction from "outside" via new. However, there is also a static function instance with which somebody can request an object instance from the factory method.
$obj = Singleton::instance();
So, the (internal) object is created only once, and then delivered afterwards until the script ends.
However, PHP ALWAYS returns the current object, even with code return $other_object;
Constructor is not a regular function. You do not return from it. If you are in __construct() you are already constructing the new object. Before proceed, I strongly recommend you read at least: https://www.php.net/manual/en/language.oop5.decon.php
You most likely need a helper method or factory that would deal with that instead. Putting the logic you tried to place into your constructor is wrong.
Also you missed to clarify what the $identifier really is. But if that's object's property, then you can expose it (i.e. via getter) and have it read for your comparison/whatever by other code.
Is there a way to get the actual calling object from a method?
For example, I have a scenario where I do something like this:
$user = new User;
$user->setName('name');
$user->save($user);
Instead of passing the $user to the save method, it would be nice to just have it get the calling object information.
I know I can do get_called_class() but that doesn't return the actual object's data. I was also looking at PHP's Reflection classes but didn't see anything exactly right.
Any ideas?
Thanks!
Within your object (and thus from within the save function itself, you can utilize the $this keyword to access the object itself. Alternatively...
public static function getInstance() {
if(!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
Where instance would be a private member variable. Make sure your constructor is private so that your object cannot be instantiated externally. <--- all this assuming php5.
I think you can do self->save($this), if save is a class method, or just save($this) if save is an instance method.
Anyway, the point is $this refers to the current instance. So setName is acts on an instance of user, so $this should equal that user inside of setName.
I'm creating a dynamic class that responds to magic __call() method. The problem is, since I'm building this on top of a already existing framework (Kohana), it checks if the method of the class exists using ReflectionClass::hasMethod, and it doesn't seem to trigger the __call() magic method for checking for it's existance. What could I do in this case? Seems like if you add the method dynamically (like $this->{$name} = function(){}) it still can't "see" it
Without more details, I'm unsure if this would suffice, however you could create proxy class to perform intermediate functionality:
class MyProxy {
protected $_object = null;
protected $_methods = array();
public function __construct($object) {
if (!is_object($object)) {
throw new InvalidArgumentException('$object must be an object');
}
$this->_object = $object;
}
public function __call($name, $arguments) {
return $this->callMethod($name, $arguments);
}
public function setMethod($name, Closure $method) {
$this->_methods[(string) $key] = $method;
}
public function callMethod($name, array $arguments) {
if (isset($this->_methods[$name])) {
return call_user_func_array($this->_methods[$name], $arguments);
}
return call_user_func_array(array($this->_object, $name), $arguments);
}
}
By calling $proxy->setMethod('foo', function () { });, you can dynamically "attach" methods to the object. When you call $proxy->foo(), it'll first do a look-up against the dynamically attached methods; if it finds one, it'll call it. Otherwise, it'll just delegate to the internal object.
Now, the problem with this approach is that attached methods aren't bound to the proxy. In other words, $this doesn't exist in the scope of an attached method.
This can be fixed though, with features from PHP 5.4+.
public function setMethod($name, Closure $method) {
$this->_methods[(string) $name] = Closure::bind($method, $this);
}
We've refined setMethod to rebind the passed closure to the proxy. Now, in the scope of an attached method, $this will point to the proxy object.
We could have rebound it to the enclosed object, but then the attached methods couldn't talk to the proxy (or other attached methods). For completeness, you'll want to add __get and __set magic, to forward property access/mutate calls to the internal object (or handle them in the proxy, whatever)
Besides, the internal object has no clue about the proxy, so it's methods (from the class definition) won't know about any of this dynamic magic anyway.
Seems like if you add the method dynamically (like $this->{$name} = function(){}) it still can't "see" it
Right, as you're creating a new property, not a method. At time of writing, PHP did not support calling anonymous functions in properties without going through __call, which isn't Reflection-friendly. Properties-with-anonymous-functions-as-methods work properly in PHP 5.4.
The only other way to add a method to a class in a way that Reflection will pick up on is using the highly experimental "runkit" extension.
An ethical question here.
I'm planning on using several manager classes in my new project that will be performing various tasks across the whole project. These classes are singletons, but require construction based on parameters.
As to when/where this construction has to happen, I have mixed feelings. I have these options so far:
Option A
It's easy to just pass these parameters to the getInstance method while having a default null value. On the very first call the parameters will be used, and any additional calls completely ignore them.
While this works, doing so feels rather unlogical, for the following reasons:
It makes documentation unclear. getInstance' first parameter must be of type Collection, but can be null... what's going on here?
You can argue that writing a line about this in the description will clear it up, but I'd prefer clarification to be unneccesary.
It feels faulty to pass getInstance any construction parameters. This is due to the fact that the method name does not explicity hint towards construction, making it unclear it will happen.
Option B
I'm thinking about a setup method. This method takes all parameters, calls the class constructor, and changes the internal class state to initialized.
When calling the getInstance method prior to setup, it will throw a NotInitializedException. After setup has been called, any additional calls to setup will result in a PreviouslyInitializedException.
After setup has been called, getInstance becomes available.
Personally, this option appeals more to me. But it feels excessive.
What option do you prefer? And why?
I would probably try and ditch the singleton approach and pass manager classes around to whatever needs them.
$manager = new Manager( $collection, $var, $var2 );
$other_class = New OtherClass( $manager );
//or
$other_class = New OtherClass;
$other_class->manager = $manager;
//or
$other_class = New OtherClass;
$other_class->setManager( $manager );
Use dependency injection to pass the Manager object around. Don't use Singleton pattern. It's a common consensus that using it creates a global state and makes your API deceptive.
PHP Global in functions (jump to answer)
Singletons are pathological liars
Inject the Manager instance to any class that needs it via the constructor. Each class should not try to instantiate Manager by themselves, the only way the classes get an instance of the Manager is by getting it from constructor.
class NeedsManager
{
protected $manager;
public function __construct(Manager $manager)
{
$this->manager = $manager;
}
}
You don't need to enforce one instance of Manager. Just don't instantiate it more than once. If all of your classes that need an instance of Manager get what they need from the constructor and never tries to instantiate it on their own, it will assure that there's just going to be one instance in your application.
How about option 3. If they are true singletons, set up properties files for their parameters for use with a no-arg getInstance.
If that doesn't fit, you might be misusing the singleton pattern.
You are looking at using a Factory design pattern. Factories are objects that act as fancy constructors for other objects. In your case, you will move setup and getInstance to the factory. The wiki article's pretty good- http://en.wikipedia.org/wiki/Factory_method_pattern
class SingletonFoo {
//properties, etc
static $singleton = NULL;
private function __constructor(){}
static function getInstance(){
if(NULL === self::$singleton) {
self::$singleton = new SingletonFoo();
}
return self::$singleton;
}
}
class FooFactory {
static $SingletonFoo = null;
static function setup($args){
if( !(NULL === self::$SingletonFoo)){
throw new AlreadyInstantiatedException();
}
self::$SingletonFoo = SingletonFoo::getInstance();
//Do stuff with $args to build SingletonFoo
return self::$SingletonFoo;
}
static function getInstance(){
if(NULL === self::$SingletonFoo) {
throw new NotInstantiatedException();
}
return self::$SingletonFoo;
}
}
Don't use Singleton, use Resources Manager (or Service Container, or DI Container):
class ResourceManager
{
protected static $resource;
public static function setResource($resource)
{
if (!empty(self::$resource)) //resource should not be overwritten
{
if ($resource!=self::$resource) return false;
else return true;
}
self::$resource = $resource;
return true;
}
public static function getResource()
{
return self::$resource;
}
}
Resource Manager allows you to set any custom classes for unit-testing (like dependency injection), you can just get needed resources without requesting them in constructor (I like DI, but sometimes it's just more handy to use empty constructors).
Ready-to-use variant: http://symfony.com/doc/current/book/service_container.html (I don't like to move logic from code to configs, but in stand-alone module it looks acceptable).
I have finally got a zend view helper working using this in my helper file:
class MY_View_Helper_Table extends Zend_View_Helper_Abstract
{
private $table_data="",$table_head="";
public function Table($data=''){
return "hello";
}
}
and this in my view:
print $this->Table();
This just prints out the returned value of the constructor, I think. How do I go about calling other methods of the class? I dont really know how to refer to the instanced object to access its methods.
I have managed to sort of do it using
method chaining, in Table I return
$this; but there must be a better and
normal way of doing it.
Actually no. Thats typically how you do it. Because of how view helpers work, if you need access to other methods on the helper then you either always return $this from your table method or you detect what to invoke by the parameter signature passed to it. For eaxmple:
public function table($options = null)
{
if(null === $options){
return $this;
}
if(is_array($options)){
return $this->tableFromArray($options);
}
// etc..
}
You can also get the helper instance with $this->getHelper('name') and then chain to the method you want... but IMO thats more confusing than doing parameter detection of just treating the default method as a getter.