Related
Class test{
function test1()
{
echo 'inside test1';
}
function test2()
{
echo 'test2';
}
function test3()
{
echo 'test3';
}
}
$obj = new test;
$obj->test2();//prints test2
$obj->test3();//prints test3
Now my question is,
How can i call another function before any called function execution?
In above case, how can i auto call 'test1' function for every another function call,
so that i can get the output as,
test1
test2
test1
test3
currently i am getting output as
test2
test3
I cannot call 'test1' function in
every function definition as there may
be many functions. I need a way to
auto call a function before calling
any function of a class.
Any alternative way would also be do.
Your best bet is the magic method __call, see below for example:
<?php
class test {
function __construct(){}
private function test1(){
echo "In test1", PHP_EOL;
}
private function test2(){
echo "test2", PHP_EOL;
}
protected function test3(){
return "test3" . PHP_EOL;
}
public function __call($method,$arguments) {
if(method_exists($this, $method)) {
$this->test1();
return call_user_func_array(array($this,$method),$arguments);
}
}
}
$a = new test;
$a->test2();
echo $a->test3();
/*
* Output:
* In test1
* test2
* In test1
* test3
*/
Please notice that test2 and test3 are not visible in the context where they are called due to protected and private. If the methods are public the above example will fail.
test1 does not have to be declared private.
ideone.com example can be found here
Updated: Add link to ideone, add example with return value.
All previous attempts are basically flawed because of http://ocramius.github.io/presentations/proxy-pattern-in-php/#/71
Here's the simple example, taken from my slides:
class BankAccount { /* ... */ }
And here's our "poor" interceptor logic:
class PoorProxy {
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($method, $args) {
return call_user_func_array(
$this->wrapped,
$args
);
}
}
Now if we have the following method to be called:
function pay(BankAccount $account) { /* ... */ }
Then this won't work:
$account = new PoorProxy(new BankAccount());
pay($account); // KABOOM!
This applies to all solutions that suggest implementing a "proxy".
Solutions suggesting explicit usage of other methods that then call your internal API are flawed, because they force you to change your public API to change an internal behavior, and they reduce type safety.
The solution provided by Kristoffer doesn't account for public methods, which is also a problem, as you can't rewrite your API to make it all private or protected.
Here is a solution that does solve this problem partially:
class BankAccountProxy extends BankAccount {
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function doThings() { // inherited public method
$this->doOtherThingsOnMethodCall();
return $this->wrapped->doThings();
}
private function doOtherThingsOnMethodCall() { /**/ }
}
Here is how you use it:
$account = new BankAccountProxy(new BankAccount());
pay($account); // WORKS!
This is a type-safe, clean solution, but it involves a lot of coding, so please take it only as an example.
Writing this boilerplate code is NOT fun, so you may want to use different approaches.
To give you an idea of how complicated this category of problems is, I can just tell you that I wrote an entire library to solve them, and some smarter, wiser, older people even went and invented an entirely different paradigm, called "Aspect Oriented Programming" (AOP).
Therefore I suggest you to look into these 3 solutions that I think may be able to solve your problem in a much cleaner way:
Use ProxyManager's "access interceptor", which is basically a proxy type that allows you to run a closure when other methods are called (example). Here is an example on how to proxy ALL calls to an $object's public API:
use ProxyManager\Factory\AccessInterceptorValueHolderFactory;
function build_wrapper($object, callable $callOnMethod) {
return (new AccessInterceptorValueHolderFactory)
->createProxy(
$object,
array_map(
function () use ($callOnMethod) {
return $callOnMethod;
},
(new ReflectionClass($object))
->getMethods(ReflectionMethod::IS_PUBLIC)
)
);
}
then just use build_wrapper as you like.
Use GO-AOP-PHP, which is an actual AOP library, completely written in PHP, but will apply this sort of logic to ALL instances of classes for which you define point cuts. This may or may not be what you want, and if your $callOnMethod should be applied only for particular instances, then AOP is not what you are looking for.
Use the PHP AOP Extension, which I don't believe to be a good solution, mainly because GO-AOP-PHP solves this problem in a more elegant/debuggable way, and because extensions in PHP are inherently a mess (that is to be attributed to PHP internals, not to the extension developers).
Additionally, by using an extension, you are making your application as un-portable as possible (try convincing a sysadmin to install a compiled version of PHP, if you dare), and you can't use your app on cool new engines such as HHVM.
Maybe it is a little bit outdated but here come my 2 cents...
I don't think that giving access to private methods via __call() is a good idea. If you have a method that you really don't want to be called outside of your object you have no way to avoid it happening.
I think that one more elegant solution should be creating some kind of universal proxy/decorator and using __call() inside it. Let me show how:
class Proxy
{
private $proxifiedClass;
function __construct($proxifiedClass)
{
$this->proxifiedClass = $proxifiedClass;
}
public function __call($methodName, $arguments)
{
if (is_callable(
array($this->proxifiedClass, $methodName)))
{
doSomethingBeforeCall();
call_user_func(array($this->proxifiedClass, $methodName), $arguments);
doSomethingAfterCall();
}
else
{
$class = get_class($this->proxifiedClass);
throw new \BadMethodCallException("No callable method $methodName at $class class");
}
}
private function doSomethingBeforeCall()
{
echo 'Before call';
//code here
}
private function doSomethingAfterCall()
{
echo 'After call';
//code here
}
}
Now a simply test class:
class Test
{
public function methodOne()
{
echo 'Method one';
}
public function methodTwo()
{
echo 'Method two';
}
private function methodThree()
{
echo 'Method three';
}
}
And all you need to do now is:
$obj = new Proxy(new Test());
$obj->methodOne();
$obj->methodTwo();
$obj->methodThree(); // This will fail, methodThree is private
Advantages:
1)You just need one proxy class and it will work with all your objects.
2)You won't disrespect accessibility rules.
3)You don't need to change the proxified objects.
Disadvantage: You will lose the inferface/contract after wrapping the original object. If you use Type hinting with frequence maybe it is a problem.
Perhaps the best way so far is to create your own method caller and wrap around whatever you need before and after the method:
class MyClass {
public function callMethod()
{
$args = func_get_args();
if (count($args) == 0) {
echo __FUNCTION__ . ': No method specified!' . PHP_EOL . PHP_EOL;;
} else {
$method = array_shift($args); // first argument is the method name and we won't need to pass it further
if (method_exists($this, $method)) {
echo __FUNCTION__ . ': I will execute this line and then call ' . __CLASS__ . '->' . $method . '()' . PHP_EOL;
call_user_func_array([$this, $method], $args);
echo __FUNCTION__ . ": I'm done with " . __CLASS__ . '->' . $method . '() and now I execute this line ' . PHP_EOL . PHP_EOL;
} else
echo __FUNCTION__ . ': Method ' . __CLASS__ . '->' . $method . '() does not exist' . PHP_EOL . PHP_EOL;
}
}
public function functionAA()
{
echo __FUNCTION__ . ": I've been called" . PHP_EOL;
}
public function functionBB($a, $b, $c)
{
echo __FUNCTION__ . ": I've been called with these arguments (" . $a . ', ' . $b . ', ' . $c . ')' . PHP_EOL;
}
}
$myClass = new MyClass();
$myClass->callMethod('functionAA');
$myClass->callMethod('functionBB', 1, 2, 3);
$myClass->callMethod('functionCC');
$myClass->callMethod();
And here's the output:
callMethod: I will execute this line and then call MyClass->functionAA()
functionAA: I've been called
callMethod: I'm done with MyClass->functionAA() and now I execute this line
callMethod: I will execute this line and then call MyClass->functionBB()
functionBB: I've been called with these arguments (1, 2, 3)
callMethod: I'm done with MyClass->functionBB() and now I execute this line
callMethod: Method MyClass->functionCC() does not exist
callMethod: No method specified!
You can even go further and create a whitelist of methods but I leave it like this for the sake of a more simple example.
You will no longer be forced to make the methods private and use them via __call().
I'm assuming that there might be situations where you will want to call the methods without the wrapper or you would like your IDE to still autocomplete the methods which will most probably not happen if you declare the methods as private.
<?php
class test
{
public function __call($name, $arguments)
{
$this->test1(); // Call from here
return call_user_func_array(array($this, $name), $arguments);
}
// methods here...
}
?>
Try adding this method overriding in the class...
If you are really, really brave, you can make it with runkit extension. (http://www.php.net/manual/en/book.runkit.php). You can play with runkit_method_redefine (you can need Reflection also to retrieve method definition) or maybe combination runkit_method_rename (old function) / runkit_method_add (new function which wraps calls to your test1 function and an old function )
The only way to do this is using the magic __call. You need to make all methods private so they are not accessable from the outside. Then define the __call method to handle the method calls. In __call you then can execute whatever function you want before calling the function that was intentionally called.
Lets have a go at this one :
class test
{
function __construct()
{
}
private function test1()
{
echo "In test1";
}
private function test2()
{
echo "test2";
}
private function test3()
{
echo "test3";
}
function CallMethodsAfterOne($methods = array())
{
//Calls the private method internally
foreach($methods as $method => $arguments)
{
$this->test1();
$arguments = $arguments ? $arguments : array(); //Check
call_user_func_array(array($this,$method),$arguments);
}
}
}
$test = new test;
$test->CallMethodsAfterOne('test2','test3','test4' => array('first_param'));
Thats what I would do
I have a similar code snippet like this
class Search
{
public function search($for, $regEx, $flag) //I would like this to be the constructor
{
// logic here
return $this;
}
}
Then I have another class that creates an object from it, later than tries to use the object.
class MyClass
{
public function start()
{
$this->search = new Search();
}
public function load()
{
$this->search($for, $regEx, $flag);
}
}
My question is, is it possible to create an object first THEN give it the parameters?
I know there are some way around this BUT I only ask because I want to use the object like this
$this->search($params);
// I have my methods chained, so I could use it in one line like
// $this->search($params)->hasResults();
if ($this->search->hasResults()) {
echo 'found stuff';
} else {
echo 'didn't find anything';
}
The way I have it set up right now, I would need to use it like this
$this->search->search($params);
if ($this->search->hasResults()) {
echo 'found stuff';
} else {
echo 'didn't find anything';
}
I have a method called search() that does the logic, and I don't want to be redundant in my naming nor do I want to change the name of the method.
I know another way to keep the visual appeal sane I could pass a variable like so
$search = $this->search->search($params);
then
$search->hasResults();
At the same time I am trying to introduce myself to new OOP concepts and learn from them. Would this require passing things by reference? or setting up some type of magic method?
While the previous anwsers show that you can, I wouldn't use it, because it breaks the concept of encapsulation. A proper way to achieve what you want is the following
class Search
{
public function __constructor($for='', $regEx='', $flag='')
{
$this->Setup($for, $regEx, $flag);
}
public function Setup($for, $regEx, $flag)
{
//assign params
//clear last result search
//chain
return $this;
}
public function search()
{
// logic here
return $this;
}
}
In this way, you can reuse the object and have the params in the constructor, without breaking encapsulation.
Yes it is possible
See the below example
<?php
class a{
public $a = 5;
public function __construct($var){
$this->a = $var;
}
}
$delta = new a(10);
echo $delta->a."\n";
$delta->__construct(15);
echo $delta->a."\n";
Output will be:
10 15
Yep, you can.
class Example {
public $any;
function __counstruct($parameters,$some_text) {
$this->any=$some_text;
return $this->any;
}
}
You can call constructor:
$obj = new Example (true,'hello');
echo $obj->any;
$obj->__construct(true,'bye-bye');
echo $obj->any;
I was able to create the visual coding I wanted by using the __call() magic method like this
public function __call($name, $params)
{
$call = ucfirst($name);
$this->$name = new $call($params);
}
from there I could use this
$this->test->search($params);
$this->test->search->hasResults();
I of course now set the search() method to the class constructor
in my class i have an array defined like this
class t {
var $settings = array();
}
I will have these settings used quite a lot, so instead of writing $this->settings['setting'] all over the place I wanted to deploy a function that automatically defines these settings in local variables.
private function get_settings () {
$array = $this->settings['array'];
$foreign_key = $this->settings['foreign_key'];
$limit = $this->settings['limit'];
$tableclassid = $this->settings['tableclassid'];
$pager = $this->settings['pager'];
$container = $this->settings['container'];
$extracolumn = $this->settings['extracolumn'];
}
now, what I want to do is to get these variables and use them in another function within class. In example
public function test () {
$this->get_settings();
return $foreign_key;
}
and I want it to return $this->settings['foreign_key']
is there a way to do this? Or do I have to clutter all the functions with that block of code get_settings() has?
I appreciate the help.. thanks :)
Use the built-in extract() function, which extracts an array into individual variables in the current scope.
extract($this->settings);
If you need modifications to these local variables to be reflected in the original array, extract them as references.
extract($this->settings, EXTR_REFS);
I can't say I would prefer to use this method myself though, or even recommend that you do it. Inside of a class, it is much more readable and understandable to keep them in the array property. In general, I never actually use extract().
Just pass it as a property. Something like this:
$class = new T();
And then:
$class->getSettings('varname');
And in function:
function get_settings($varname){
return $this->settings[$varname];
}
Or use the __get() overload function:
public function __get($name){
return $this->settings[$name];
}
And call it like this:
$class->varname;
(non existing function/variable in class, will be sent to the get() overload function
You can always overload the magic functions:
<?php
class DynamicSettings
{
/**
* Stores the settings
* #var array
**/
protected $settings = array();
/**
* Called when something like this:
* $dynset->name = value;
* is executed.
**/
public function __set($name, $value)
{
$this->settings[$name] = $value;
}
/**
* Called when something like this:
* $value = $dynset->name;
* is executed.
**/
public function __get($name)
{
if (array_key_exists($name, $this->settings))
{
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error('Undefined dynamic property ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
/**
* Called when checking for variable existance
**/
public function __isset($name)
{
return isset($this->settings[$name]);
}
/**
* Called when unsetting some value.
**/
public function __unset($name)
{
unset($this->settings[$name]);
}
}
$dynset = new DynamicSettings();
$dynset->hello = "Hello "; // creates array key "hello" with value "Hello "
$dynset->world = "World!"; // creates array key "world" with value "World!"
echo $dynset->hello . $dynset->world; // outputs "Hello World!"
Try extending the "DynamicSettings" class and using those keys as class members now.
I have abandoned all hope of ever being able to overload my constructors in PHP, so what I'd really like to know is why.
Is there even a reason for it? Does it create inherently bad code? Is it widely accepted language design to not allow it, or are other languages nicer than PHP?
You can't overload ANY method in PHP. If you want to be able to instantiate a PHP object while passing several different combinations of parameters, use the factory pattern with a private constructor.
For example:
public MyClass {
private function __construct() {
...
}
public static function makeNewWithParameterA($paramA) {
$obj = new MyClass();
// other initialization
return $obj;
}
public static function makeNewWithParametersBandC($paramB, $paramC) {
$obj = new MyClass();
// other initialization
return $obj;
}
}
$myObject = MyClass::makeNewWithParameterA("foo");
$anotherObject = MyClass::makeNewWithParametersBandC("bar", 3);
You can use variable arguments to produce the same effect. Without strong typing, it doesn't make much sense to add, given default arguments and all of the other "work arounds."
For completeness, I'll suggest Fluent Interfaces. The idea is that by adding return $this; to the end of your methods you can chain calls together. So instead of
$car1 = new Car('blue', 'RWD');
$car2 = new Car('Ford', '300hp');
(which simply wouldn't work), you can do:
$car = (new Car)
->setColor('blue')
->setMake('Ford')
->setDrive('FWD');
That way you can pick exactly which properties you want to set. In a lot of ways it's similar to passing in an array of options to your initial call:
$car = new Car(['make' => 'Ford', 'seats' => 5]);
PHP Manual: Function Arguments, Default Values
I have overcome this simply by using default values for function parameters. In __constuct, list the required parameters first. List the optional parameters after that in the general form $param = null.
class User
{
private $db;
private $userInput;
public function __construct(Database $db, array $userInput = null)
{
$this->db = $db;
$this->userInput = $userInput;
}
}
This can be instantiated as:
$user = new User($db)
or
$user = new User($db, $inputArray);
This is not a perfect solution, but I have made this work by separating parameters into absolutely mandatory parameters no matter when the object is constructed, and, as a group, optional parameters listed in order of importance.
It works.
True overloading is indeed unsupported in PHP. As #Pestilence mentioned, you can use variable arguments. Some people just use an Associative Array of various options to overcome this.
they say this work:
<?php
class A
{
function __construct()
{
$a = func_get_args();
$i = func_num_args();
if (method_exists($this,$f='__construct'.$i)) {
call_user_func_array(array($this,$f),$a);
}
}
function __construct1($a1)
{
echo('__construct with 1 param called: '.$a1.PHP_EOL);
}
function __construct2($a1,$a2)
{
echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
}
function __construct3($a1,$a2,$a3)
{
echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
}
}
$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');
// results:
// __construct with 1 param called: sheep
// __construct with 2 params called: sheep,cat
// __construct with 3 params called: sheep,cat,dog
?>
and, it seem every one are happy with it, but for me it didn't work...
if you get it to work, its one kind of overloading too...
it take all argoments and pass them to the secondary function constructor...
<?php
//php do not automatically call parent class constructor at all if child class has constructor so you have to call parent class constructor explicitly, however parent class constructor is called automatically if child class has no constructor
class MyClass
{
function construct1($value1)
{
echo "<br/> dummy constructor is called with 1 arguments and it is $value1";
}
function construct2($value1,$value2)
{
echo "<br/> dummy constructor is called with 2 arguments and it is $value1, $value2";
}
function construct3($value1,$value2,$value3)
{
echo "<br/> dummy constructor is called with 3 arguments and it is $value1, $value2 , $value3";
}
public function __construct()
{
$NoOfArguments = func_num_args(); //return no of arguments passed in function
$arguments = func_get_args();
echo "<br/> child constructor is called $NoOfArguments";
switch ($NoOfArguments) {
case 1:
self::construct1($arguments[0]);
break;
case 2:
self::construct2($arguments[0],$arguments[1]);
break;
case 3:
self::construct3($arguments[0],$arguments[1],$arguments[2]);
break;
default:
echo "Invalid No of arguments passed";
break;
}
}
}
$c = new MyClass();
$c2 = new MyClass("ankit");
$c2 = new MyClass("ankit","Jiya");
$c2 = new MyClass("ankit","Jiya","Kasish");
?>
You can use conditional statements in your constructor and then perform your task.
Eg.
class Example
{
function __construct($no_of_args)
{// lets assume 2
switch($no_of_args)
{
case 1:
// write your code
break;
case 2:
//write your 2nd set of code
break;
default:
//write your default statement
}
}
}
$object1 = new Example(1); // this will run your 1st case
$object2 = new Example(2); // this will run your 2nd case
and so on...
You can of course overload any function in PHP using __call() and __callStatic() magic methods.
It is a little bit tricky, but the implementation can do exactly what your are looking for.
Here is the resource on the official PHP.net website:
https://www.php.net/manual/en/language.oop5.overloading.php#object.call
And here is the example which works for both static and non-static methods:
class MethodTest
{
public function __call($name, $arguments)
{
// Note: value of $name is case sensitive.
echo "Calling object method '$name' "
. implode(', ', $arguments). "\n";
}
/** As of PHP 5.3.0 */
public static function __callStatic($name, $arguments)
{
// Note: value of $name is case sensitive.
echo "Calling static method '$name' "
. implode(', ', $arguments). "\n";
}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context'); // As of PHP 5.3.0
And you can apply this to constructors by using the following code in the __construct():
$clsName = get_class($this);
$clsName->methodName($args);
Pretty easy.
And you may want to implement __clone() to make a clone copy of the class with the method that you called without having the function that you called in every instance...
Adding this answer for completeness with respect to current PHP , since later versions of PHP , you can in fact overload constructors in a way . Following code will help to understand ,
<?php
class A
{
function __construct()
{
$a = func_get_args();
$i = func_num_args();
if (method_exists($this,$f='__construct'.$i)) {
call_user_func_array(array($this,$f),$a);
}
}
function __construct1($a1)
{
echo('__construct with 1 param called: '.$a1.PHP_EOL);
}
function __construct2($a1,$a2)
{
echo('__construct with 2 params called: '.$a1.','.$a2.PHP_EOL);
}
function __construct3($a1,$a2,$a3)
{
echo('__construct with 3 params called: '.$a1.','.$a2.','.$a3.PHP_EOL);
}
}
$o = new A('sheep');
$o = new A('sheep','cat');
$o = new A('sheep','cat','dog');
?>
Output :
__construct with 1 param called: sheep
__construct with 2 params called: sheep,cat
__construct with 3 params called: sheep,cat,dog
In this case I recommend using Interfaces:
interface IExample {
public function someMethod();
}
class oneParamConstructor implements IExample {
public function __construct(private int $someNumber) {
}
public function someMethod(){
}
}
class twoParamConstructor implements IExample {
public function __construct(private int $someNumber, string $someString) {
}
public function someMethod(){
}
}
than in your code:
function doSomething(IExample $example) {
$example->someMethod();
}
$a = new oneParamConstructor(12);
$b = new twoParamConstructor(45, "foo");
doSomething($a)
doSomething($b)
As far as I know, constructor overloading in PHP is not allowed, simply because the developers of PHP did not include that functionality - this is one of the many complaints about PHP.
I've heard of tricks and workarounds, but true overloading in the OOP sense is missing. Maybe in future versions, it will be included.
I think we can also use constructor with default arguments as a potential substitute to constructor overloading in PHP.
Still, it is really sad that true constructor overloading is not supported in PHP.
<?php
class myClass {
public $param1 = 'a';
public $param2 = 'b';
public function __construct($param1 = NULL, $param2 = NULL) {
if ($param1 == NULL && $param2 == NULL) {
// $this->param1 = $param1;
// $this->param2 = $param2;
} elseif ($param1 == NULL && $param2 !== NULL) {
// $this->param1 = $param1;
$this->param2 = $param2;
} elseif ($param1 !== NULL && $param2 == NULL) {
$this->param1 = $param1;
// $this->param2 = $param2;
} else {
$this->param1 = $param1;
$this->param2 = $param2;
}
}
}
// $myObject = new myClass();
// $myObject = new myClass(NULL, 2);
$myObject = new myClass(1, '');
// $myObject = new myClass(1, 2);
echo $myObject->param1;
echo "<br />";
echo $myObject->param2;
?>
public function construct1($user , $company)
{
dd("constructor 1");
$this->user = $user;
$this->company = $company;
}
public function construct2($cc_mail , $bcc_mail , $mail_data,$user,$company)
{
dd('constructor 2');
$this->mail_data=$mail_data;
$this->user=$user;
$this->company=$company;
$this->cc_mail=$cc_mail;
$this->bcc_mail=$bcc_mail;
}
public function __construct()
{
$NoOfArguments = func_num_args(); //return no of arguments passed in function
$arguments = func_get_args();
switch ($NoOfArguments) {
case 1:
self::construct1($arguments[0]);
break;
case 5:
self::construct2($arguments[0],$arguments[1],$arguments[2],$arguments[3],$arguments[4]);
break;
default:
echo "Invalid No of arguments passed";
break;
}
I'm really no OOP expert, but as I understand it overloading means the ability of a method to act differently depending in the parameters it receives as input. This is very much possible with PHP, you just don't declare the input types since PHP does not have strong typing, and all the overloading is done at runtime instead of compile time.
PHP
mysql database
I have created a follow on question to this one here that is specifically about pagination
I need to call a method from one class in another, and be able to change the method that is called. Like so
class db{
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class YourClass {
var $fcn;
$db = new db()
function invoke(){
call_user_func($this->fcn);
}
}
$instance = new YourClass;
$instance->fcn = 'db->a';
$instance->invoke();
I want to use a method 'a' from the db class in the 'yourClass' method 'invoke'
Thanks
Ok this is what i have put together from the answers provided and it works.
class A {
function a(){
$x = 'Method a is used';
return $x;
}
function b(){
$x = 'Method b is used';
return $x;
}
}
class B {
function invoke($obj, $method){
echo call_user_func( array( $obj, $method) );
}
}
$instance = new B();
$instance->invoke(new A(),"a");
Which writes, 'Method a is used' to the screen
But i really want to be able to pass arguments to method "a" so i tried the code below.
class A {
function a($var1,$var2,$var3){
$x = 'the three passed values are ' . $var1 . ' and ' . $var2 . ' and ' . $var3;
return $x;
}
function b(){
$x = 'im method b';
return $x;
}
}
class B {
function invoke($obj,$arguments){
echo call_user_func_array($obj,$arguments);
}
}
$arguments = array('apple','banana','pineapple');
$use_function = array(new A(),"a");
$instance = new B();
$instance->invoke($use_function,$arguments);
It almost works but i get these errors above the correct answer
Missing argument 1 for A::a(),.....for argument 2 and 3 as well but then the answer prints to the screen
"the three passed values are apple and banana and pineapple"
I'm probably making a rookie mistake I've been coding all day. If someone could fix the script above and submit the working code, I would be eternally grateful. I have to put this issue to bed so i can go to bed.
Thanks
As of PHP5.3 you could use closures or functors to pass methods around. Prior to that, you could write an anonymous function with create_function(), but that is rather awkward.
Basically, what you are trying to do could be done with the Strategy Pattern.
removed example code, as it wasn't helpful anymore after the OP changed the question (see wiki)
Apart from that, you might want to look into Fowlers's Data Source Architectural Patterns. The Zend Framework (and pretty much all other PHP frameworks) offers database access classes you could use for these patterns and there is also a paginator class, so why not check them out to learn how they did it.
removed EDIT 1 as it wasn't helpful anymore after the OP changed the question (see wiki)
EDIT 2
Ok, let's take a step by step approach to this (not using a Strategy Pattern though)
What you are asking for in the question can easily be solved with this code:
class Foo
{
public function bar()
{
echo 'bar method in Foo';
}
}
class MyInvoker
{
protected $myObject;
public function __construct()
{
$this->myObject = new Foo();
}
public function __call($method, $args)
{
$invocation = array($this->myObject, $method);
return call_user_func_array($invocation, $args);
}
}
With this code you'd just call the appropriate methods. No setting of methods names. No clumsy extra invoke method. No reinventing of how methods are called. You dont need it, because PHP has the __call function that you just taught to send all methods not existing in MyInvoker to $myObject, e.g. Foo.:
$invoker = new MyInvoker;
$invoker->bar(); // outputs 'bar method in Foo called'
You might just as well have extended MyInvoker to be a subclass of Foo, e.g.
class MyInvoker extends Foo {}
and then you could do the same. This not what you need though and it illustrates how pointless it is, to do such a thing. MyInvoker now does nothing by itself. It is an empty class and effectively the same as Foo. Even with the previous approach using the __call method it is not doing anything. This is why I have asked you to be more specific about the desired outcome, which is a Paginator.
First try:
class Paginator()
{
// A class holding all possible queries of our application
protected $queries;
// A class providing access to the database, like PDO_MySql
protected $dbConn;
public function __construct()
{
$this->db = new MyPdo();
$this->queries = new DbQueries();
}
public function __call($method, $args)
{
$invocation = array($this->queries, $method);
$query = call_user_func_array($invocation, $args);
return $this->dbConn->query($query);
}
}
With that code, our Paginator creates everything it needs inside itself, tightly coupling the db connection class and all queries and it allows you to call upon these, like so
$paginator = new Paginator;
// assuming we have something like getImageCount() in DbQueries
echo $paginator->getImageCount();
What is happening then is, Paginator will recognize it doesnt know getImageCount() and will invoke the __call method. The __call method will try to invoke the getImageCount() method on the DbQueries. Since it exists, it will return the query, which in turn is passed to the db connection to execute it. Great you'd say, but it's not. In fact, this is horrible. Your paginator's responsibility is to count items in a table and fetch items from this table in a certain range and amount. But right now, it is not doing anything like this. It is completely oblivious to whats going on, so lets try a new class:
class Paginator
{
protected $dbConn;
protected $itemCount;
public function __construct($dbConn)
{
$this->dbConn = $dbConn;
}
public function countItems($query)
{
$this->itemCount = $this->dbConn->query('select count(*) from (?)', $query);
return $this->itemCount;
}
public function fetchItems($query, $offset = 0, $limit = 20)
{
$sql = sprintf('select * from (?) LIMIT %d, %d', $offset, $limit);
return $this->dbConn->query($sql, $query);
}
}
Much better. Now our Paginator is an aggregate instead of a composite, meaning it does not instantiate objects inside itself, but requires them to be passed to it in the constructor. This is called dependency injection (and also provides a loose coupling, when dbConn uses an interface) which will make your app much more maintainable, as it is easy to exchange components now. This will also come in handy when Unit Testing your code.
In addition, your Paginator now concentrates on what it is supposed to do: counting and fetching items of an arbitrary query. No need to pass methods around. No need for obscure method invocation. You'd use it like this:
$paginator = new Paginator($dbConn);
$query = $dbQueries->findImagesUploadedLastWeek(); // returns SQL query string
$images = $paginator->countItems($query);
if($images > 0) {
$images = $paginator->fetchItems($query);
}
And that's it. Well, almost. You'd have to render the pagination of course. But this should be rather trivial, if you extend what you already have above. The $imageCount property is a hint at where to go next.
Anyway, hope that I could shed some light.
P.S. The $this->dbConn->query($sql, $query) calls are of course dummy code. Dont expect to be able to copy and paste it and get it working. In addition, you should make sure the queries added to the Paginator SQL is safe to use. You wouldnt want someone to insert a query that deletes all your db rows. Never trust user input.
P.P.S. $query should be an SQL query string. Check the PHP manual for PDO::prepare. In general, it yields better performance and security to prepare a statement before executing it. The page in the manual will give you the clues about the ? in the query calls. If you dont want to use PDO, just use sprintf() or str_replace() to replace ? with $query, e.g. $this->dbConn->query(sprintf('SELECT count(*) from (%s)', $query) but keep in mind that this has none of the benefits of a prepared statement and potentially opens the door for SQL Injection vulnerabilities.
P.P.P.S Yes, Dependency Injection is generally a preferred strategy. This is an advanved topic though and might be too much to fully grasp right now, but it's well worth looking into it. For now, it should be enough if you try to favor favor aggregation over composition. Your classes should only do what they are responsible for and get any dependencies through the constructor.
Here are two ways of doing it:
class YourClass {
var $fcn;
function invoke($arguments){
//one way:
$this->{$this->fcn}($arguments);
//another way:
call_user_func_array(array($this, $this->fcn), $arguments);
}
function a(){
echo 'I am a()';
}
}
$instance = new YourClass;
$instance->fcn = 'a';
$instance->invoke();
This'll print out "I am a()" from inside the class.
you are almost there
class db {
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class YourClass {
var $fcn;
function __construct() {
$this->db = new db();
}
function invoke() {
call_user_func(array(
$this->{$this->fcn[0]},
$this->fcn[1]
));
}
}
$instance = new YourClass;
$instance->fcn = array('db', 'a');
$instance->invoke();
$instance->fcn = array('db', 'b');
$instance->invoke();
the syntax is quite fancy, but it works
// edit: from your comment it looks like the simplest option is to pass method name as string, like this
class Paginator {
function __consturct($db, $counter_func) ...
function get_count() {
$args = func_get_args();
return call_user_func_array(
array($this->db, $this->counter_func),
$args);
}
}
new Paginator($db, 'get_num_products');
I am guessing that you are using php here. Php supports variable functions which might solve you problem but as far as I am aware does not support delegates/function pointers.
What database are you using? I would be against putting queries within the code and using stored procedures as an alternative, if this is supported in the database you are using. This may solve the underlying problem you have.
Are you asking if PHP has functional references? It doesn't. But it does let you call functions by putting their name in a string, or an array of a class name and method name.
See call_user_func() for a description, and variable functions.
class DB {
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class B {
protected $db;
private $method;
function __constructor($db) { $this->db; }
function invoke($m){
$this->method = $m;
// Non static call
call_user_func( array( $this->db, $this->method ) );
}
}
$db = new DB();
$b = new B($db);
$b->invoke('a');
I have made little modifications to my initial answer. You could also check out this post, it may help:
Database and OOP Practices in PHP
The Observer Design Pattern may be useful for this sort of thing, or it might be a misuse of the pattern; I don't know yet. Anyway, for your consideration:
class DbObserver implements SplObserver
{
public function update(SplSubject $subject) // Required
{
$method = $subject->getObserverMethod();
$args = $subject->getObserverArgs();
$this->$method($args);
}
private function a($args)
{
echo 'I run query ' . $args[0] . '<br />';
}
private function b($args)
{
echo 'I run query ' . $args[0] . ' because ' . $args[1] . '<br />';
}
private function c()
{
echo 'I have no argument' . '<br />';
}
}
class ObserverObserver implements SplObserver
{
public function update(SplSubject $subject) // Required
{
if (count($subject->getAttached()) > 1) {
echo 'I saw that<br />';
} else {
echo 'Nothing happened<br />';
}
}
}
class DbSubject implements SplSubject
{
private $observerMethod;
private $observerArgs = array();
private $attached = array();
public function notify() // Required
{
foreach ($this->attached as $each) {
$each->update($this);
}
}
public function attach(SplObserver $observer) // Required
{
$this->attached[] = $observer;
}
public function detach(SplObserver $observer) // Required
{
$key = array_keys($this->attached, $observer);
unset($this->attached[$key[0]]);
}
public function setObserverMethod($method, $args = array())
{
$this->observerMethod = $method;
$this->observerArgs = $args;
return $this;
}
public function getObserverMethod()
{
return $this->observerMethod;
}
public function getObserverArgs()
{
return $this->observerArgs;
}
public function getAttached()
{
return $this->attached;
}
}
$db_subj = new DbSubject;
$db_obs = new DbObserver;
$db_subj->attach($db_obs);
$args = array('A');
$db_subj->setObserverMethod('a', $args)->notify();
$args = array('B', 'I can');
$db_subj->setObserverMethod('b', $args)->notify();
$obsvr = new ObserverObserver;
$db_subj->attach($obsvr);
$db_subj->setObserverMethod('c')->notify();
$db_subj->detach($db_obs);
$db_subj->notify();
/**
I run query A
I run query B because I can
I have no argument
I saw that
Nothing happened
**/
You need to make this change:
$arguments = array('apple','banana','pineapple');
$a = new A();
$use_function = array(&$a,"a"); // Make this changes to your code
$instance = new B();
$instance->invoke($use_function,$arguments);
class A {
function a(){ echo 'I run a query';}
function b(){ echo 'im the other query';}
}
class B {
function Test() {
invoke(new $A(), "a");
}
function invoke($obj, $method){
// Non static call
call_user_func( array( $obj, $method ) );
// Static call
//call_user_func( array( 'ClassName', 'method' ) );
}
}
I hope this helps.