Lets say I have:
class Zebra{
public static function action(){
print 'I was called from the '.get_class().' class'; // How do I get water here?
}
}
class Water{
public static function drink(){
Zebra::action();
}
}
Water::drink();
How do I get "water" from the zebra class?
(This is for php 5.3)
You can get the caller's info from debug_backtrace http://php.net/manual/en/function.debug-backtrace.php
One not so good solution is :
use __METHOD__ or __FUNCTION__ or __CLASS__ .
and pass it as parameter to function being called.
http://codepad.org/AVG0Taq7
<?php
class Zebra{
public static function action($source){
print 'I was called from the '.$source.' class'; // How do I get water here?
}
}
class Water{
public static function drink(){
Zebra::action(__CLASS__);
}
}
Water::drink();
?>
Full usable solution using exception, but not debug_backtrace, no need to modify any prototype :
function getRealCallClass($functionName)
{
try
{
throw new exception();
}
catch(exception $e)
{
$trace = $e->getTrace();
$bInfunction = false;
foreach($trace as $trace_piece)
{
if ($trace_piece['function'] == $functionName)
{
if (!$bInfunction)
$bInfunction = true;
}
elseif($bInfunction) //found !!!
{
return $trace_piece['class'];
}
}
}
}
class Zebra{
public static function action(){
print 'I was called from the '.getRealCallClass(__FUNCTION__).' class';
}
}
class Water{
public static function drink(){
Zebra::action();
}
}
Water::drink();
Related
I could use RefexionFunction outside of a class, but inside a class I get an exception.
Fatal error: Uncaught ReflectionException: Function Test::test_function() does not exist in test.php.
<?php
function parameters($functionName,$args){
$f = new ReflectionFunction($functionName);
....
}
class Test{
public function test_functionA($abc,$d,$e,$f) {
parameters(__METHOD__,func_get_args());
}
protected function test_functionB($abc,$d,$e,$f) {
parameters(__METHOD__,func_get_args());
}
private function test_functionC($abc,$d,$e,$f) {
parameters(__METHOD__,func_get_args());
}
}
$test = new Test();
$test->test_function('something',1,2,array(123,456));
?>
Appreciate your help.
Your error:
Fatal error: Uncaught ReflectionException: Function Test::test_function() does not exist in test.php.
Doesn't refer to the function name quite as you expect it to.
ReflectionClass docs says this:
The ReflectionClass class reports information about a class.
ref: https://secure.php.net/manual/en/class.reflectionclass.php
You want to use a combination of methods available in that class to get information about the passed method like this:
public function parameters($class, $fnc)
{
$f = new ReflectionClass($class);
if ($f->hasMethod($fnc)) {
return 'howdy folks';
} else {
return 'not so howdy folks';
}
}
You first pass the class before checking if the function exists. You can then use the built-in function hasMethod to check if the function exists. You then use the parameters function like this:
public function testFunction()
{
return $this->helper->parameters(__CLASS__, __FUNCTION__);
}
All together the code looks like this:
<?php
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
error_reporting(-1);
class paramsHelper
{
public function parameters($class, $fnc)
{
$f = new ReflectionClass($class);
$f->getMethod($fnc);
if ($f->hasMethod($fnc)) {
return 'howdy folks';
} else {
return 'not so howdy folks';
}
return $f;
}
}
class Test
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function testFunction()
{
return $this->helper->parameters(__CLASS__, __FUNCTION__);
}
}
$test = new Test(new paramsHelper());
echo '<pre>';
print_r($test->testFunction());
echo '</pre>';
One of your other problems is that __METHOD__ actually returns a string like this: Test::testFunction not testFunction - hence my use of __FUNCTION__ instead.
Edit:
To get the parameters of the passed method, change your parameters method to this:
class paramsHelper
{
public function getMethodParameters($class, $fnc)
{
$f = new ReflectionMethod($class, $fnc);
echo '<pre>';
print_r($f->getParameters());
echo '</pre>';
}
}
This uses ReflectionMethod in place of ReflectionClass - this is more inline with your intended use.
ref: https://secure.php.net/manual/en/class.reflectionmethod.php
use:
class paramsHelper
{
public function getMethodParameters($class, $fnc)
{
$f = new ReflectionMethod($class, $fnc);
echo '<pre>';
print_r($f->getParameters());
echo '</pre>';
}
}
class Test
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function testFunction($a = '', $b = 1, $c = 3)
{
return $this->helper->parameters(__CLASS__, __FUNCTION__);
}
}
$test = new Test(new paramsHelper());
echo '<pre>';
print_r($test->testFunction());
echo '</pre>';
I'm new to the OOP model of PHP and encountered a problem recently.
When Object B is thrown in Object A and calls a function of B, is it possible to get Object A inside the function of B?
Consider the following code example:
$test = new A();
class A
{
public function __construct () {
$arg = "something";
try{
throw new B($arg);
}
catch (B $e) {
$e->bar();
}
}
public function foo($arg){
//do something
}
}
class B extends Exception
{
public $arg;
public function __construct ($arg) {
$this->arg = $arg;
}
public function bar(){
// do something
// .....
// this is not correct, $this is not object A
// so how do i do this?
$this->foo($arg);
}
}
You can pass a caller object into a constructor:
throw new B($this);
Your B constructor will place it into $arg:
public $arg; // better call it $caller or somewhat
public function __construct ($arg) {
$this->arg = $arg;
}
Then, in a method of B you can access it:
public function bar(){
$this->arg->foo(""); // pass an argument, which is expected at function foo($arg){}
}
Here is the working IDEOne demo.
Also, #Sumurai8 has implemented a good PHPFiddle demo. It extends your constructor so that it passes both argument and caller.
It is not the best approach, as you assume that Exception B is thrown from the class that have method foo, which may not alway be the case.
I would recommend to keep it simple:
$test = new A();
class A
{
public function __construct () {
$arg = "something";
try{
throw new B($arg);
}
catch (B $e) {
$e->bar();
$this->foo($arg);
}
}
public function foo($arg){
//do something
}
}
class B extends Exception
{
public $arg;
public function __construct ($arg) {
$this->arg = $arg;
}
public function bar(){
// do something
// .....
// this is correct now
}
}
I'm trying to use call_user_func to call a method from another method of the same object, e.g.
class MyClass
{
public function __construct()
{
$this->foo('bar');
}
public function foo($method)
{
return call_user_func(array($this, $method), 'Hello World');
}
public function bar($message)
{
echo $message;
}
}
new MyClass; Should return 'Hello World'...
Does anyone know the correct way to achieve this?
Many thanks!
The code you posted should work just fine. An alternative would be to use "variable functions" like this:
public function foo($method)
{
//safety first - you might not need this if the $method
//parameter is tightly controlled....
if (method_exists($this, $method))
{
return $this->$method('Hello World');
}
else
{
//oh dear - handle this situation in whatever way
//is appropriate
return null;
}
}
This works for me:
<?php
class MyClass
{
public function __construct()
{
$this->foo('bar');
}
public function foo($method)
{
return call_user_func(array($this, $method), 'Hello World');
}
public function bar($message)
{
echo $message;
}
}
$mc = new MyClass();
?>
This gets printed out:
wraith:Downloads mwilliamson$ php userfunc_test.php
Hello World
new MyClass; Should return 'Hello World'...
A constructor does not return anything.
Please look at the following code snipped
class A
{
function __get($name)
{
if ($name == 'service') {
return new Proxy($this);
}
}
function render()
{
echo 'Rendering A class : ' . $this->service->get('title');
}
protected function resourceFile()
{
return 'A.res';
}
}
class B extends A
{
protected function resourceFile()
{
return 'B.res';
}
function render()
{
parent::render();
echo 'Rendering B class : ' . $this->service->get('title');
}
}
class Proxy
{
private $mSite = null;
public function __construct($site)
{
$this->mSite = $site;
}
public function get($key)
{
// problem here
}
}
// in the main script
$obj = new B();
$obj->render();
Question is: in method 'get' of class 'Proxy', how I extract the corresponding resource file name (resourceFile returns the name) by using only $mSite (object pointer)?
What about:
public function get($key)
{
$file = $this->mSite->resourceFile();
}
But this requires A::resourceFile() to be public otherwise you cannot access the method from outside the object scope - that's what access modifiers have been designed for.
EDIT:
OK - now I think I do understand, what you want to achieve. The following example should demonstrate the desired behavior:
class A
{
private function _method()
{
return 'A';
}
public function render()
{
echo $this->_method();
}
}
class B extends A
{
private function _method()
{
return 'B';
}
public function render()
{
parent::render();
echo $this->_method();
}
}
$b = new B();
$b->render(); // outputs AB
But if you ask me - I think you should think about your design as the solution seems somewhat hacky and hard to understand for someone looking at the code.
I'm looking for something like break for loops.
Here's some example code (using Symfony's lime) where stop() would not let the class continue and I_DONT_WANT_THIS_TO_RUN() would not be executed.
$browser->isStatusCode(200)
->isRequestParameter('module', 'home')
->isRequestParameter('action', 'index')
->click('Register')
->stop()
->I_DONT_WANT_THIS_TO_RUN();
$browser->thenThisRunsOkay();
Calling $this->__deconstruct(); from within stop() doesn't seem to do the trick. Is there a function I can call within stop() that would make that happen?
You could use PHP exceptions:
// This function would of course be declared in the class
function stop() {
throw new Exception('Stopped.');
}
try {
$browser->isStatusCode(200)
->isRequestParameter('module', 'home')
->isRequestParameter('action', 'index')
->click('Register')
->stop()
->I_DONT_WANT_THIS_TO_RUN();
} catch (Exception $e) {
// when stop() throws the exception, control will go on from here.
}
$browser->thenThisRunsOkay();
Just return another class which will return $this for every method called.
Example:
class NoMethods {
public function __call($name, $args)
{
echo __METHOD__ . " called $name with " . count($args) . " arguments.\n";
return $this;
}
}
class Browser {
public function runThis()
{
echo __METHOD__ . "\n";
return $this;
}
public function stop()
{
echo __METHOD__ . "\n";
return new NoMethods();
}
public function dontRunThis()
{
echo __METHOD__ . "\n";
return $this;
}
}
$browser = new Browser();
echo "with stop\n";
$browser->runThis()->stop()->dontRunThis()->dunno('hey');
echo "without stop\n";
$browser->runThis()->dontRunThis();
echo "the end\n";
Will result in:
with stop
Browser::runThis
Browser::stop
NoMethods::__call called dontRunThis with 0 arguments.
NoMethods::__call called dunno with 1 arguments.
without stop
Browser::runThis
Browser::dontRunThis
the end
OIS's answer is really good, though I could see that it might get confusing if the object suddenly changes to something else. That is, you'd expect that at the end of your chain, you'll end up with the same object. To avoid that problem, I'd add a private variable to tell the class whether or not to actually do anything. If the class has been stopped, then every class just returns $this straight away. This gives you the added benefit of being able to restart the execution.
class MyClass {
private $halt;
function __call($func, $args) {
if ($this->halt) {
return $this;
} else {
return $this->$func($args);
}
}
private function isRequestParameter() {
// ...
}
public function stop() {
$this->halt = true;
}
public function start() {
$this->halt = false;
}
}
This could be put into a parent class so you don't have to duplicate this code.