PHP: Get the name of the calling function? - php

Is there a way to get the name of the calling function in PHP?
In the following code I am using the name of the calling function as part of an event name. I would like to modify the getEventName() function so that it can automatically determine the name of the calling method. Is there a php function that does this?
class foo() {
public function bar() {
$eventName = $this->getEventName(__FUNCTION__);
// ... do something with the event name here
}
public function baz() {
$eventName = $this->getEventName(__FUNCTION__);
// ... do something with the event name here
}
protected function getEventName($functionName) {
return get_class($this) . '.' . $functionName;
}
}

Have a look at the output of debug_backtrace().

if you want to know the function that called whatever function you are currently in, you can define something like:
<?php
/**
* Returns the calling function through a backtrace
*/
function get_calling_function() {
// a function x has called a function y which called this
// see stackoverflow.com/questions/190421
$caller = debug_backtrace();
$caller = $caller[2];
$r = $caller['function'] . '()';
if (isset($caller['class'])) {
$r .= ' in ' . $caller['class'];
}
if (isset($caller['object'])) {
$r .= ' (' . get_class($caller['object']) . ')';
}
return $r;
}
?>

Related

Pass PHP Class as Parameter

How can i pass a class as a parameter in my function
So far i've tried
$sc = new SampleClass();
SampleFunction($sc);
function SampleFunction(&$refClass)
{
echo $refClass->getValue();
}
this is a simplified example of what im doing.. i actually have to do complex procedures inside this sample function. I'm not getting any response from the sample function. What am i doing wrong? thank you
UPDATE
char.php
class Charss {
var $name=0;
var $hp=500;
var $spd=10;
var $rtime=10;
var $dmg=10;
function __construct( $name, $hp, $spd, $rtime , $dmg) {
$this->name = $name;
$this->hp = $hp;
$this->spd = $spd;
$this->rtime = $rtime;
$this->dmg = $dmg;
}
function get_name() {
return $this->name;
}
function set_name($new_name) {
$this->name = $new_name;
}
function get_hp() {
return $this->hp;
}
function set_hp($new_hp) {
$this->hp = $new_hp;
}
function get_spd() {
return $this->spd;
}
function set_spd($new_spd) {
$this->spd = $new_spd;
}
function get_rtime() {
return $this->rtime;
}
function set_rtime($new_rtime) {
$this->rtime = $new_rtime;
}
function get_dmg() {
return $this->get_dmg;
}
function set_dmg($new_dmg) {
$this->dmg = $new_dmg;
}
}
myclass.php
require("char.php");
class Person {
function try_process()
{
$chr1 = new Charss("Player1",500,3,0,50);
$chr2 = new Charss("Player2",500,6,0,70);
while ($chr1->get_hp() > 0 && $chr2->get_hp() > 0)
{
$sth = min($chr1->get_rtime(), $chr2->get_rtime());
if ($chr1->get_rtime() == 0 && $chr2->get_rtime() > 0)
{
exit;
Fight($chr1,$chr2);
$chr1->set_rtime($chr1->get_spd());
}
elseif ($chr2->get_rtime() == 0 && $chr1->get_rtime() > 0)
{
Fight($chr2,$chr1);
$chr2->set_rtime($chr2->get_spd());
}
else
{
Fight($chr1,$chr2); #having trouble with this
$chr1->set_rtime($chr1->get_spd());
}
$chr1->set_rtime($chr1->get_rtime() - $sth);
$chr2->set_rtime($chr2->get_rtime() - $sth);
}
}
function Fight($atk,$def)
{
$def->set_hp($def->get_hp() - $atk->get_dmg());
echo $atk->get_name() . " attacked " . $def->get_name() . " for " . $atk->get_dmg() . " damage";
}
}
so im calling the function try_process on button click
What you're actually doing there is passing an object, not a class.
$sc = new SampleClass();
creates an instance of SampleClass, aka an object.
I assume there's some error being thrown elsewhere as what you have is correct.
I tested the following code and got the expected output:
class SampleClass
{
public function getValue()
{
return 4;
}
}
$sc = new SampleClass();
SampleFunction($sc);
function SampleFunction(&$refClass)
{
echo $refClass->getValue();
}
Output: 4
If you provide more details of your actual code we might be able to determine the problem.
I can't see anything wrong with your code
using &$refClass is however is not recommended and I guess willbe removed from future iteration of PHP version
but here is an example
class objects are passed as reference I suppose so no need of '&'
http://ideone.com/GbmUy
Why is the function argument a reference? Probably shouldn't be.
Other than that, there's nothing wrong with you posted, so the error is likely within SampleClass.
Others have answered pretty well, but this is a silly little example to show you how to modify the class (either by calling a property setter, or setting public properties directly)
class foo {
private $member1;
public $member2;
public function __construct($member1,$member2) {
$this->member1=$member1;
$this->member2=$member2;
}
public function SetMember1($value) {
$this->member1 = $value;
}
public function GetMember1() {
return $this->member1;
}
}
function SetMembers(foo $obj, $member1, $member2) {
// Call a setter
$obj->SetMember1($member1);
// Set a member variable directly
$obj->member2 = $member2;
}
$obj = new foo('default member 1', 'default member 2');
echo "member1 (before): {$obj->GetMember1()}\n";
echo "member2 (before): {$obj->member2}\n";
// Change values
SetMembers($obj, 'new member1', 'new member2');
echo "member1 (after): {$obj->GetMember1()}\n";
echo "member2 (after): {$obj->member2}\n";
This will output:
member1 (before): default member 1
member2 (before): default member 2
member1 (after): new member1
member2 (after): new member2

PHP Action each function preforms

I have the following situation.
I have a class with a lot of functions. Each function starts with executing the same method. Is there a way that I can like implement this method into the function so that it is executed automatically?
Here is an example:
class test
{
static function_1($param) {some_method($param); other stuff....}
static function_2($param) {some_method($param); other stuff then function 1....}
static function_3($param) {some_method($param); other stuff then function 1 and function 2....}
}
So is there a way to execute some_method(); automaticly without declaring it in each function?
Thanks in advance!
Whole code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/*
* The Assets Library
*
* This class let's you add assets (javascripts, stylesheets and images) way easier..
*/
class Assets {
private $css_url;
private $js_url;
private $img_url;
public function __construct()
{
$CI =& get_instance();
$CI->config->load('assets');
$asset_url = base_url() . $CI->config->item('assets_dir');
$this->css_url = $asset_url . $CI->config->item('css_dir_name');
$this->js_url = $asset_url . $CI->config->item('js_dir_name');
$this->img_url = $asset_url . $CI->config->item('img_dir_name');
}
// Returns the css html link
public function css_html_link($filename)
{
// Check whether or not a filetype was given
$filename = $this->_add_filetype($filename, 'css');
$link = '<link type="text/css" rel="stylesheet" href="' . $this->css_url . $filename . '" />';
return $link;
}
// Returns the css link
public function css_link($filename)
{
$filename = $this->_add_filetype($filename, 'css');
return $this->css_url . $filename;
}
// Returns the js html link
public function js_html_link($filename)
{
$filename = $this->_add_filetype($filename, 'js');
$script = '<script type="text/javascript" src="' . $this->js_url . $filename . '"></script>';
return $script;
}
// Return the js link
public function js_link($filename)
{
$filename = $this->_add_filetype($filename, 'js');
return $this->js_url . $filename;
}
// Returns the image html tag
public function img_html_link($filename, $rel = NULL)
{
// Get the filename without the filetype
$alt_text = substr($filename, 0, strpos($filename, '.')+1);
$alt_text = 'alt="'.$alt_text.'"';
// If relation is giving, use it
$img_rel = ($rel !== FALSE) ? 'rel="' . $rel . '"' : '';
$image = '<img src="' . $this->img_url . $filename . '" '.$rel.' ' . $alt_text . '/>';
return $image;
}
// Return the image link
public function img_link($filename)
{
return $this->img_url . $filename;
}
// Check whether or not a filetype was specified in $file, if not, it will be added
private function _add_filetype($file, $type)
{
if(strpos($file, '.' . $type) === FALSE)
{
$file = $file . '.' . $type;
}
return $file;
}
}
/* End of file assets.php */
/* Location: ./application/libraries/assets.php */
every time you initiate the class, it calls the __construct() function, or in PHP 4 (I hope you are not using php 4) it uses the function with the same name as the class
If you do this, it should work for every initiate of the class:
function __construct($param){
some_method($param);
}
if you call multiple functions in the same initiation of the class, you could do this:
var $param;
function __construct($param){
$this->param = $param;
}
function doMethod(){
some_method($this->param);
}
function function_1()
{
$this->doMethod();
}
Calling the class multiple times, with different params. Perhaps try this approach:
function __call($function, $param){
some_method($param);
switch ($function){
case 'function1':
$this->function1($param);
break;
/// etc..
}
}
I'm afraid that in this case the answer is 'no'.
You're not 'declaring' some_method() each time, you are calling it. If you don't call it, it can't run, so you have to call it each time.
Cut & paste.....
Why not paste your actual code here, some refactoring may help.
Edit after seeing actual code
I can't see the problem with your existing code. It is clear and you will know what it does in a year's time. I would keep it as it is. The answer you accepted will work, but it is obfuscating your code. You will have problems working out what you did and why you did it in when you come back to maintain your code in the future.
You could create a class containing an instance of the class test (composition) and implement its __call magic method. Something akin to:
class testWrapper
{
private $test;
function __construct()
{
$this->test = new Test();
}
function __call($name, $args)
{
call_user_func_array(array($this->test, 'some_method'), $args);
call_user_func_array(array($this->test, $name), $args);
}
}
You then call methods from the test class on the instance object of testWrapper.
You can further refine the logic in the __call method to only call some_method() based on the passed-in method name, etc.

How to add methods dynamically

I'm trying to add methods dynamically from external files.
Right now I have __call method in my class so when i call the method I want, __call includes it for me; the problem is I want to call loaded function by using my class, and I don't want loaded function outside of the class;
Class myClass
{
function__call($name, $args)
{
require_once($name.".php");
}
}
echoA.php:
function echoA()
{
echo("A");
}
then i want to use it like:
$myClass = new myClass();
$myClass->echoA();
Any advice will be appreciated.
Is this what you need?
$methodOne = function ()
{
echo "I am doing one.".PHP_EOL;
};
$methodTwo = function ()
{
echo "I am doing two.".PHP_EOL;
};
class Composite
{
function addMethod($name, $method)
{
$this->{$name} = $method;
}
public function __call($name, $arguments)
{
return call_user_func($this->{$name}, $arguments);
}
}
$one = new Composite();
$one -> addMethod("method1", $methodOne);
$one -> method1();
$one -> addMethod("method2", $methodTwo);
$one -> method2();
You cannot dynamically add methods to a class at runtime, period.*
PHP simply isn't a very duck-punchable language.
* Without ugly hacks.
You can dynamically add attributes and methods providing it is done through the constructor in the same way you can pass a function as argument of another function.
class Example {
function __construct($f)
{
$this->action=$f;
}
}
function fun() {
echo "hello\n";
}
$ex1 = new class('fun');
You can not call directlry $ex1->action(), it must be assigned to a variable and then you can call this variable like a function.
if i read the manual right,
the __call get called insted of the function, if the function dosn't exist
so you probely need to call it after you created it
Class myClass
{
function __call($name, $args)
{
require_once($name.".php");
$this->$name($args);
}
}
You can create an attribute in your class : methods=[]
and use create_function for create lambda function.
Stock it in the methods attribute, at index of the name of method you want.
use :
function __call($method, $arguments)
{
if(method_exists($this, $method))
$this->$method($arguments);
else
$this->methods[$method]($arguments);
}
to find and call good method.
What you are referring to is called Overloading. Read all about it in the PHP Manual
/**
* #method Talk hello(string $name)
* #method Talk goodbye(string $name)
*/
class Talk {
private $methods = [];
public function __construct(array $methods) {
$this->methods = $methods;
}
public function __call(string $method, array $arguments): Talk {
if ($func = $this->methods[$method] ?? false) {
$func(...$arguments);
return $this;
}
throw new \RuntimeException(sprintf('Missing %s method.'));
}
}
$howdy = new Talk([
'hello' => function(string $name) {
echo sprintf('Hello %s!%s', $name, PHP_EOL);
},
'goodbye' => function(string $name) {
echo sprintf('Goodbye %s!%s', $name, PHP_EOL);
},
]);
$howdy
->hello('Jim')
->goodbye('Joe');
https://3v4l.org/iIhph
You can do both adding methods and properties dynamically.
Properties:
class XXX
{
public function __construct($array1)
{
foreach ($array1 as $item) {
$this->$item = "PropValue for property : " . $item;
}
}
}
$a1 = array("prop1", "prop2", "prop3", "prop4");
$class1 = new XXX($a1);
echo $class1->prop1 . PHP_EOL;
echo $class1->prop2 . PHP_EOL;
echo $class1->prop3 . PHP_EOL;
echo $class1->prop4 . PHP_EOL;
Methods:
//using anounymous function
$method1 = function () {
echo "this can be in an include file and read inline." . PHP_EOL;
};
class class1
{
//build the new method from the constructor, not required to do it here by it is simpler.
public function __construct($functionName, $body)
{
$this->{$functionName} = $body;
}
public function __call($functionName, $arguments)
{
return call_user_func($this->{$functionName}, $arguments);
}
}
//pass the new method name and the refernce to the anounymous function
$myObjectWithNewMethod = new class1("method1", $method1);
$myObjectWithNewMethod->method1();
I've worked up the following code example and a helper method which works with __call which may prove useful. https://github.com/permanenttourist/helpers/tree/master/PHP/php_append_methods

PHP subclass can't access public variable set by parent

I'm very new to PHP and OOP in general. I'm using codeigniter for a framework, and am currently attempting to build a class 'BuildLinks' that will redirect the user to the correct link based on what URL they landed on.
The controller passes the right variables to the class, while the function build_afflink() selects what class to call next based on the var1
The controller:
function out($var1, $var2)
{
// redirect to link after parsing data
$debug='1';
$params = array('var1'=>$var1, 'var2'=>$var2);
$this->load->library('BuildLinks', $params);
if ($debug=='0'){
$redirect = $this->buildlinks->build_afflink();
redirect($redirect, 'location', 301);
}
else {
var_dump($this->buildlinks->build_afflink());
}
}
The class BuildLinks is a work in progress... but it is extended by all of the other sites I need to support.
BuildLinks class:
class BuildLinks
{
public $var1;
public $var2;
public $link;
function __construct($params)
{
//populate up inititial variables from $params array (passed from controller)
$this->var1 = (string)$params['var1'];
$this->var2 = (string)$params['var2'];
echo __class__ . ' loaded....' . 'var1: '.$this->var1 . ' var2: ' .$this->var2. '<br/>';
}
public function get_var1()
{
return $this->var1;
}
public function set_var1($var1)
{
$this->var1 = $var1;
}
public function get_var2()
{
return $this->var2;
}
public function set_var2($var2)
{
$this->var2 = $var2;
}
function build_thelink()
{
switch ($this->var1) {
case 'amazon':
//echo 'Amazon is our vendor.<br>';
$newobj = new Amazon;
// Amazon subclass returns the correct affiliate link
return $newobj->affiliate_link();
break;
case 'ebay':
$newobj = new Ebay;
//ebay subclass however, cannot access the public var var1, it returns a null value for $this->var1
return $newobj->affiliate_link();
break;
}
}
}
Essentially, when I make a new Ebay object, it can't access any of the public variables from the parent class BuildLinks. What am I doing wrong?
In this example, I have it construct the initial variables and echo back out some information for debugging.
EDIT: If I change the __construct to read:
function __construct($params)
{
//populate up inititial variables from $params array (passed from controller)
$this->var1 = (string)$params['var1'];
$this->var2 = (string)$params['var2'];
echo __class__ . ' loaded....' . 'var1: '.$this->var1. ' var2: ' .$this->var2 . '<br/>';
var_dump(get_class_vars(get_class($this)));
}
Then I get this as an output:
BuildLinks loaded....var1: ebay var2: somedata
array
'var1' => null
'var2' => null
'link' => null
The following works fine:
$newobj = new Amazon;
return $newobj->affiliate_link();
This does not, but the classes are almost identical...
$newobj = new Ebay;
return $newobj->affiliate_link();
Here is the ebay class:
class Ebay extends BuildLinks
{
private $res;
//TODO: language/multiple site support
//public $locale;
function __construct()
{
//echo __class__ . ' loaded....' . 'vendor: '.$this->vendor . ' isbn: ' .$this->isbn . '<br/>';
}
function add_to_cart_button($isbn, $locale)
{
}
function affiliate_link()
{
$this->get_info();
return $this->link;
}
// Load $this->api_call() and return Amazon SimpleXML response object, load variables
function get_info()
{
$apicall = $this->api_call();
//build variables
foreach ($apicall->searchResult->item as $item) {
$this->link = (string )$item->viewItemURL;
}
}
// Generate API call and return simplexml object $res
function api_call()
{
//build $apicall here
$res = simplexml_load_file($apicall);
// Check to see if the request was successful, else print an error
if ($res->ack == "Success") {
$this->res = $res;
return $res;
} else {
echo 'api_call() unsuccessful';
}
}
}
This part of the ebay class doesn't make too much sense to me:
function api_call()
{
//build $apicall here
$res = simplexml_load_file($apicall);
// Check to see if the request was successful, else print an error
if ($res->ack == "Success") {
$this->res = $res;
return $res;
} else {
echo 'api_call() unsuccessful';
}
}
simplexml_load_file($apicall); specifically isn't loading anything. Where's $apicall? If you debug that, does it echo "api_call() unsuccessful"?
Because if that's unsuccessful then Ebay::link won't be set and as a result return $newobj->affiliate_link(); wouldn't end up returning anything.
for clarification, $apicall should be set to a file path before calling simplexml_load_file($apicall);
Ebay::get_info() does this
foreach ($apicall->searchResult->item as $item) {
$this->link = (string )$item->viewItemURL;
}
If $apicall->searchResult->item happens to have several elements, last of which is empty, you'll get only that last empty element.
That's a wild guess though.
[added]
You're overwriting a constructor in Ebay, so if you want BuildLinks' class constructor to be called, you need to do this explicitly.

Name and parameters of parent function

I'm trying to figure out how to get the name and parameters of a parent function.
Example:
function foo($a,$b){
bar();
}
function bar(){
// Magic Print
}
foo('hello', 'world');
Output:
foo('hello','world')
Any tips?
You can get the information from debug_backtrace().
function bar(){
$backtrace = debug_backtrace();
$t = $backtrace[1];
print $t["function"] . "('" . implode("','", $t["args"]) . "')\n";
}

Categories