PHP class problem - php

class Gdn {
const AliasDispatcher = 'Dispatcher';
protected static $_Factory = NULL;
public static function Dispatcher() {
$Result = self::Factory(self::AliasDispatcher);
return $Result;
}
public static function Factory($Alias = FALSE) {
if ($Alias === FALSE)
return self::$_Factory;
// Get the arguments to pass to the factory.
//$Args = array($Arg1, $Arg2, $Arg3, $Arg4, $Arg5);
$Args = func_get_args();
array_shift($Args);
return self::$_Factory->Factory($Alias, $Args);
}
}
If I call the Dispatcher() like $Dispatcher = Gdn::Dispatcher();, what does return self::$_Factory->Factory($Alias, $Args); mean?

It means Dispatcher() is returning an object, and that object is a copy of something created by Factory().

self:: means you are refering to the class of the current object
since factory is a recursive function it will keep calling itself until it runs out of arguments and then returns the factory that is set in the current factory class.
if you would do:
"blah->Factory('test1','test2',test3','test4')" it would run like:
blah->factory('test1','test2','test3','test4')
blah->$_Factory->Factory('test1',array('test2','test3','test4'))
blah->$_Factory->Factory(array('test2','test3','test4'));
blah->$_Factory->Factory();
// oh hey, i dont have any arguments, replace it with my default argument 'false' and thus return the factory
return self::$_Factory;
i dont know WHY you would want it, but this is what it does

Related

Return variable class name from php factory

I've got a factory that I want to return a ::class from. However, the factory could potentially return a couple dozen different types (determined by the type of object passed into the factory), named TypeOneObject, TypeTwoObject etc. Is it possible to return the class using a variable, something like this?
$type = $myrequiredclass->getType();
return $type."Object"::class; // wanting TypeOneObject::class
It seems like no matter how I construct this return statement I always get PHP Parse error: syntax error, unexpected '::'
I know it'd be easy enough to do with a big if/then or switch but I'd like to avoid that.
Here's a more fleshed out scenario:
class TypeOneObject
{
public static function whoAreYou()
{
return 'Type One Object!';
}
}
class MyRequiredClass
{
public function getType()
{
return 'TypeOne';
}
}
class MyFactory
{
public static function getFactoryObject(MyRequiredClass $class)
{
$type = $class->getType()."Object";
return $type::class;
}
}
$object = MyFactory::getFactoryObject(new MyRequiredClass());
$object::whoAreYou();
The best way to get the class name from the $type instance is to use php get_class_methods function. This will get us all the methods inside the class instance. from there we can filter and use call_user_func to call the method and get the right values.
class TypeOneObject
{
public static function whoAreYou()
{
return 'Type One Object!';
}
}
class MyRequiredClass
{
public function getType()
{
return 'TypeOne';
}
}
class MyFactory
{
public static function getFactoryObject(MyRequiredClass $class)
{
$methods = get_class_methods($class);
$method = array_filter($methods, function($method) {
return $method == 'getType';
});
$class = new $class();
$method = $method[0];
$methodName = call_user_func([$class, $method]);
$objectName = sprintf('%sObject', $methodName);
return new $objectName;
}
}
$object = MyFactory::getFactoryObject(new MyRequiredClass());
echo $object::whoAreYou();
Output
Type One Object!

Pass Method form different class for later execution to a second class

I have to classes and I want to pass a method fro classA to classB constructor and store it on a classB instance variable so as to execute it later.
class A
{
public function execute()
{
$ClassB = new ElgEmpAnalyticsImporterControllerImporterEmporioOrder(ConMW::getDB(), $this -> lPointFile, [$this, 'getLastPoints'] );
$ClassB -> import();
}
public function getLastPoints(Array $keys)
{
$res = [];
forEach ( json_decode( file_get_contents($this -> lastPointFile) ) as $key => $value ):
if ( in_array($key, $keys) ):
$res[$key] = $value;
else:
$res[$key] = '';
endif;
endforeach;
unset($key);
unset($value);
return $res;
}
}
classB
{
public $getLastPoints = null;
public function __construct(callable $getLastPoints)
{
$this -> getLastPoints = $getLastPoints;
}
public function import()
{
$lastPoints = $this -> getLastPoints(['idOrder', 'orderLastExport']);
}
}
Trying to execute it like that I get the error "Call to undefined method getLastPoints()"
I think the problem is on storing the function on the instance variable $getLastPoints of classB. I can conclude this because If I execute the function on the constructor it works. That means if I change the constructor of classB like this
classB
{
public $getLastPoints = null;
public function __construct(callable $getLastPoints)
{
$getLastPoints(['idOrder', 'orderLastExport']);
}
}
it works.
But what i need is to execute the external function inside the import function.
Can someone please help me?
thanks for your time,
Edit for clarification: My question is why I can execute the function inside the contructor like this :
$lastPoint(a,b)
but when I assign the callable into an instance variable like this:
$this -> lastPoint(a,b)
it does not work.
I read that php uses different storage for variables and function. PHP probably sees the callable $lastPoints as a variable. So can the callable $lastPoints, be added as dynamic function to my instance of classB?
Christoforos
PHP Callable Object as Object Member
$getlastpoints is a property with an array value stored in it, not a function.
call_user_func_array($this->getlastpoints, ['idOrder', 'orderLastExport']);
public function import()
{
$my_func = $this->getLastPoints;
$lastPoints = $my_func(['idOrder', 'orderLastExport']);
}
In a nutshell, the reason you will have to do this is because you can define properties and methods in a PHP class having the same name. e.g.
class foo {
public $bar
public function bar() {}
}
so in this instance, if allowed to directly access a stored callable on the $bar property... What would the call below reference?
$my_foo_obj->bar()
To avoid the situation, you cannot call it directly.

Is there a way to perform this with PHP? Static methods

Looks like impossible do something like that:
class Service
{
public function get($argument1, $argument2)
{
echo "Arguments: $argument1, $argument2.";
}
}
class Main
{
public function __callStatic($method, $arguments)
{
$method = "get".ucfirst($method);
$arguments = implode(", ", $arguments);
$object = self::$method;
return $object->get($arguments);
}
public static function getService()
{
return new Service;
}
}
$main = new Main;
$main::Service("1", "2"); // Should output 'Arguments: 1, 2.'
The problem is the self mess everthing, with this will be possible.
Use self::$variable, self this we have a variable in the Main context.
$object = self don't work too.
$object = clone self dont' work too.
define("method", $method);
self::method will not work because self thinks is a class constant property.
this can't be used.
Is there a way to perform this?
Just pass the original $arguments directly to call_user_func_array:
return call_user_func_array(array($object, "get"), $arguments);
Then, just make a static call to Main:
Main::service("1", "2");
See it here in action: http://codepad.viper-7.com/phhqy6

Overloading construct in php?

does php (5.3.7) supports overloading ?
Example:
class myclass{
function __construct($arg1) { // Construct with 1 param}
function __construct($arg1,$arg2) { // Construct with 2 param}
}
new myclass(123); //> call the first construct
new myclass(123,'abc'); //> call the second
You have to implement the constructor once and use func_get_args and func_num_args like this:
<?php
class myclass {
function __construct() {
$args = func_get_args();
switch (func_num_args()) {
case 1:
var_dump($args[0]);
break;
case 2:
var_dump($args[0], $args[1]);
break;
default:
throw new Exception("Wrong number of arguments passed to the constructor of myclass");
}
}
}
new myclass(123); //> call the first construct
new myclass(123,'abc'); //> call the second
new myclass(123,'abc','xyz'); //> will throw an exception
This way you can support any number of arguments.
No, but it supports optional parameters, or variable number of parameters.
class myclass{
function __construct($arg1, $arg2 = null){
if($arg2 === null){ // construct with 1 param //}
else{ // construct with 2 param //}
}
}
Note that this has the downside that if you actually want to be able to supply null as a second parameter it will not accept it. But in the remote case you want that you can always use the func_* family of utils.
I would define some fromXXX methods that call the __constructor which takes a parameter like id.
<?php
class MyClass {
public function __construct(int $id) {
$instance = Database::getByID($id);
$this->foo = $instance['foo'];
$this->bar = $instance['bar'];
}
public static function fromFoo(string $foo): MyClass {
$id = Database::find('foo', $foo);
return new MyClass($id);
}
}

PHP[OOP] - How to call class constructor manually?

Please see the code bellow:
01. class Test {
02. public function __construct($param1, $param2, $param3) {
03. echo $param1.$param2.$param3;
04. }
05. }
06.
07. $params = array('p1','p2','p3');
08.
09. $ob = new Test;
10.
11. if(method_exists($ob,'__construct')) {
12. call_user_func_array(array($ob,'__construct'),$params);
13. }
Now, the problem is the constructor is called in line 09
But i want to call it manually at line 11-13
Is it possible? If then how? Any idea please?
It is not possible to prevent the constructor from being called when the object is constructed (line 9 in your code). If there is some functionality that happens in your __construct() method that you wish to postpone until after construction, you should move it to another method. A good name for that method might be init().
Why not just do this?
class Test {
public function __construct($param1, $param2, $param3) {
echo $param1.$param2.$param3;
}
}
$ob = new Test('p1', 'p2', 'p3');
EDIT: I just thought of a hacky way you could prevent a constructor from being called (sort of). You could subclass Test and override the constructor with an empty, do-nothing constructor.
class SubTest extends Test {
public function __construct() {
// don't call parent::__construct()
}
public function init($param1, $param2, $param3) {
parent::__construct($param1, $param2, $param3);
}
}
$ob = new SubTest();
$ob->init('p1', 'p2', 'p3');
This is might make sense if you're dealing with some code that you cannot change for some reason and need to work around some annoying behavior of a poorly written constructor.
Note that if the constructor (__construct method) contains arguments passed by
reference, then the function:
call_user_func_array
will fail with an error.
I suggest you to use Reflection class instead; here is how you can do so:
// assuming that class file is already included.
$refMethod = new ReflectionMethod('class_name_here', '__construct');
$params = $refMethod->getParameters();
$re_args = array();
foreach($params as $key => $param)
{
if ($param->isPassedByReference())
{
$re_args[$key] = &$args[$key];
}
else
{
$re_args[$key] = $args[$key];
}
}
$refClass = new ReflectionClass('class_name_here');
$class_instance = $refClass->newInstanceArgs((array) $re_args);
I don't know if there are some security concerns by using the eval() method, but you could make yourself a function like this:
function CreateObj($className,$params)
{
$strArgs = '$params['.implode('],$params[',array_keys($params)).']';
eval('$ob = new '.$className.'('.$strArgs.');');
return $ob
}
And now $ob should now be defined with its correct parameters, i haven't tested it and maybe there is a mistake in the code, but you get the idea....
If separating instantiation from initialization isn't strictly a requirement, there are two other possibilities: first, a static factory method.
class Test {
public function __construct($param1, $param2, $param3) {
echo $param1.$param2.$param3;
}
public static function CreateTest($param1, $param2, $param3) {
return new Test($param1, $param2, $param3);
}
}
$params = array('p1','p2','p3');
if(method_exists($ob,'__construct')) {
call_user_func_array(array($ob,'CreateTest'),$params);
}
Or, if you're using php 5.3.0 or higher, you can use a lambda:
class Test {
public function __construct($param1, $param2, $param3) {
echo $param1.$param2.$param3;
}
}
$params = array('p1','p2','p3');
$func = function ($arg1, $arg2, $arg3) {
return new Test($arg1, $arg2, $arg3);
}
if(method_exists($ob,'__construct')) {
call_user_func_array($func, $params);
}
The initialization method described by Asaph is great if for some reason you have a need to logically separate initialization from instantiation, but if supporting your use case above is a special case, not a regular requirement, it can be inconvenient to require users to instantiate and initialize your object in two separate steps.
The factory method is nice because it gives you a method to call to get an initialized instance. The object is initialized and instantiated in the same operation, though, so if you have a need to separate the two it won't work.
And lastly, I recommend the lambda if this initialization mechanism is uncommonly used, and you don't want to clutter your class definition with initialization or factory methods that will hardly ever be used.
In PHP you can create objects w/o calling the constructor. But that does not work by using new but by un-serializing an object instance.
The constructor can then be called manually.
Normally this is not needed. See as well: Loading an object from PHP session, does it call constructor?
<?php
class Test
{
public function __construct()
{
echo '__construct called.',"\n";
}
}
function old($class)
{
return unserialize
(
sprintf
(
'O:%d:"%s":0:{}',
strlen($class),
$class
)
);
}
$test = old('Test');
$test->__construct();
to construct your object first and then pass parameters your could try this way:
class Test {
public function __CONSTRUCT($p1="Bundy", $p2="house", $p3=10) {
$this->init($p1, $p2, $p3);
}
public function init($p1, $p2, $p3) {
//setup your object here
}
}
then it is possible to construct the object and call
$ob->init($p1, $p2, $p3);
later.
I see no reason why the constructor should be deferred so this below still achieves what you probably want and even better because the constructor will by default be called on object instantiation.
class Test {
public function __construct()
{
}
public function init($param1, $param2, $param3){
echo $param1.$param2.$param3;
}
}
$params = array('p1','p2','p3');
$ob = new Test();
if(method_exists($ob,'init')) {
call_user_func_array(array($ob,'init'),$params);
}

Categories