How do I call PHP parent methods from within an inherited method? - php

In PHP, I'm trying to reference a method defined in an object's parent class, from a method inherited from the object's parent class. Here's the code:
class base_class {
function do_something() {
print "base_class::do_something()\n";
}
function inherit_this() {
parent::do_something();
}
}
class middle_class extends base_class {
function do_something() {
print "middle_class::do_something()\n";
}
}
class top_class extends middle_class {
function do_something() {
print "top_class::do_something()\n";
$this->inherit_this();
}
}
$obj = new top_class;
$obj->do_something();
The problem is that parent::do_something() in inherit_this() tries to find the parent class of base_class, not the parent of the object's actual class, and the example above throws an error. Is there something I can write instead of parent::do_something() that would call middle_class::do_something(), and that would still work even in classes that extend (say) top_class?

To get it work you can modify your base_class like this:
class base_class {
function do_something() {
print "base_class::do_something()\n";
}
function inherit_this() {
$this->do_something();
}
}
Then your top_clas will call inherit_this() of your base class, but there will be a recursion: do_something() of top_class calls $this->inherit_this(), and in base_class you call again $this->do_something() (in your base class $this will reference to your top_class). Because of that, you will call inherit_this() over and over again.
You should rename the methods to prevent that.
Update
If you want that base_class inherit_this() prints "base_class::do_something" you could modify your base_class like this:
class base_class {
function do_something() {
print "base_class::do_something()\n";
}
function inherit_this() {
base_class::do_something();
}
}
In this case you make a static call to the base_class method do_something(). The output is top_class::do_something() base_class::do_something()
Update 2
Regarding to your comment you can modify your base_class like this:
class base_class {
function do_something() {
print "base_class::do_something()\n";
}
function inherit_this() {
$par = get_parent_class($this);
$par::do_something();
}
}
You get the parrent class of $this and then call the method. Output will be: top_class::do_something() middle_class::do_something()

I'll start by saying "Thank you" to grrbrr404. He gave me some ideas and got me started in the right direction.
The solution I finally settled on was the following:
function inherit_this() {
$bt = debug_backtrace();
call_user_func(array($this, get_parent_class($bt[1]['class']) . '::do_something'));
}
It's not pretty (I particularly hate calling debug_backtrace() for this), but it keeps the object context set to $this, and handles the case where the method is called from a method somewhere in the middle of the object hierarchy.
For those who found my example confusing and/or wanted to know "Why would you want to do this?" I apologize, and provide the following additional example, which is hopefully more illustrative and closer to the original problem. It is considerably longer, but hopefully shows why I care about keeping $this set properly, and also shows why I can't hard-code any particular class name or use $this->method(). Avoiding infinite loops is always a priority with me. :-)
class class1_required_type { }
class class2_required_type { }
class class3_required_type { }
class class4_required_type { }
class class1 {
protected $data = array();
protected function checkType($name, $value, $requiredType) {
print "In class1::checkType()\n";
if (get_class($value) === $requiredType) {
$backtrace = debug_backtrace();
call_user_func(array($this, get_parent_class($backtrace[1]['class']) . "::mySet"), $name, $value);
} else {
throw new Exception(get_class($this) . "::mySet('" . $name . "') requires an object of type '" . $requiredType . "', but got '" . get_class($value) . "'");
}
}
function mySet($name, $value) {
print "In class1::mySet()\n";
if ($name === 'class1_field') {
$this->checkType($name, $value, 'class1_required_type');
} else {
$this->data[$name] = $value;
}
}
function dump() {
foreach ($this->data as $key => $value) {
print "$key: " . get_class($value) . "\n";
}
}
}
class class2 extends class1 {
function mySet($name, $value) {
print "In class2::mySet()\n";
if ($name === 'class2_field') {
$this->checkType($name, $value, 'class2_required_type');
} else {
parent::mySet($name, $value);
}
}
}
class class3 extends class2 {
function mySet($name, $value) {
print "In class3::mySet()\n";
if ($name === 'class3_field') {
$this->checkType($name, $value, 'class3_required_type');
} else {
parent::mySet($name, $value);
}
}
}
class class4 extends class3 {
function mySet($name, $value) {
print "In class4::mySet()\n";
if ($name === 'class4_field') {
$this->checkType($name, $value, 'class4_required_type');
} else {
parent::mySet($name, $value);
}
}
}
$obj = new class4;
$obj->mySet('class3_field', new class3_required_type);
$obj->dump();
I'm trying to avoid duplication of the "checkType()" function, yet still provide correct behavior even when the hierarchy gets arbitrarily large.
More elegant solutions are, of course, most welcome.

Im not quite sure I understands why you want to do like this. But I guess you cannot do it. I understand that you will be able to make a middle_class2 and be able inherit from that, and then it would be middle_class2 instead of middle_class's dosomething you call?!
So I guess you'll need to create the
function inherit_this() {
parent::do_something();
}
in the middle_class.
I thought about a get_class($this)::parent::do_something().. but that didn't work.
Just to be sure.. You want to call middle_class::do_something() right??

class base_class {
function do_something() {
print "base_class::do_something()\n";
}
function inherit_this() {
//parent::do_something();
$class = get_called_class();
$class::do_something();
}
}

Related

PHP: is current function running inside an object?

Is there any way in PHP to tell if a function is being run from inside or outside a particular class of object?
function getToDaChoppa() {
if( "we're inside the Choppa object" ) {
$foo = "We're inside";
} else {
$foo = "We're outside";
}
echo $foo;
}
class Choppa() {
public function getStatus() {
getToDaChoppa();
}
}
Running:
getToDaChoppa();
( new Choppa )->getStatus();
should echo:
We're Outside
We're Inside
A function on it's own doesn't know, if it's called from an class or not and that for good reasons. If the function behaves different, that would lead to very unmaintainable code and hard debugging, etc.
If you need the calling function and maybe ask, if this function belongs to an class/get the instance, the only way to do this is debug_backtrace. But in general you really only should do this, for debug code (as the name tells you).
Normally you would just have two functions for each case, or pass an parameter, which stores the desired information.
You could always pass an instance of the class through on top of a default value, then evaluate on the return of a get_class().
function getToDaChoppa($that = false) {
$class = $that ? get_class($that) : '';
if($class == "Choppa") {
$foo = "We're inside";
} else {
$foo = "We're outside";
}
echo $foo;
}
class Choppa {
public function getStatus() {
getToDaChoppa($this);
}
}
getToDaChoppa(); // Would return "We're outside"
( new Choppa )->getStatus(); // Would return "We're inside"
See https://ideone.com/WWg1Hl for a working example.
You might use debug_backtrace(), go back one hop, and check if that was made from inside a class. I'm not sure I'd do this in production though...
function getToDaChoppa() {
$bt = debug_backtrace();
if (isset($bt[1]) && array_key_exists('class', $bt[1])) {
echo "called from class\n";
} else {
echo "called directly\n";
}
}
Clarification: if you want it to trigger only for one specific class:
function getToDaChoppa() {
$bt = debug_backtrace();
if (
isset($bt[1]) &&
array_key_exists('class', $bt[1]) &&
$bt[1]['class'] === 'Choppa'
) {
echo "called from class Choppa\n";
} else {
echo "called otherwise\n";
}
}

In PHP is it possible to tell if a method is called explicitly by parent::

Is it possible to tell if a method in the parent class is called explicitly by parent:: as opposed to being called automatically because the sub class doesn't contain the method being called?
well, I'm not sure you can get it easily.
anyway, I think you could follow one of this way, if you need a work-around:
example 1:
class Par
{
function printit($which = false)
{
// when you call this method, based on variable it tells how it was called
if ($which) {
echo "called with parent \n";
} else {
echo "called with this \n";
}
}
}
class Chi extends Par
{
function callParent()
{
parent::printit(TRUE);
}
function callFunction()
{
$this->printit(FALSE);
}
}
$chi = new Chi();
$chi->callParent();
$chi->callFunction();
example 2:
class Par
{
function printit()
{
// get all functions in child class
$child_methods = array_diff(get_class_methods("Chi") , get_class_methods("Par"));
// if the function there is in child class, probably it was called from there
if (in_array(__FUNCTION__, $child_methods)) {
echo "called with child \n";
} else {
echo "called with parent \n";
}
}
}
class Chi extends Par
{
function callParent()
{
parent::printit();
}
function callFunction()
{
$this->printit();
}
}
$chi = new Chi();
$chi->callParent();
$chi->callFunction();

can I pass __construct parameters after a class has been instantiated into an object?

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

Is it possible to use mixins in php

I came to know about mixins.So my doubt is, is it possible to use mixins in php?If yes then how?
Use Trait introduced in PHP 5.4
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
which prints Hello World!
http://php.net/manual/en/language.oop5.traits.php
This answer is obsolete as of PHP 5.4. See Jeanno's answer for how to use traits.
It really depends on what level of mixins you want from PHP. PHP handles single-inheritance, and abstract classes, which can get you most of the way.
Of course the best part of mixins is that they're interchangeable snippets added to whatever class needs them.
To get around the multiple inheritance issue, you could use include to pull in snippets of code. You'll likely have to dump in some boilerplate code to get it to work properly in some cases, but it would certainly help towards keeping your programs DRY.
Example:
class Foo
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
class Fizz
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
It's not as direct as being able to define a class as class Foo mixin Bar, but it should get you most of the way there. There are some drawbacks: you need to keep the same parameter names and return variable names, you'll need to pass other data that relies on context such as func_get_args_array or __FILE__.
Mixins for PHP (PHP does not implement Mixins natively, but this library will help)
First google result for "php5 mixin": http://www.sitepoint.com/forums/php-application-design-147/ruby-like-mixins-php5-332491.html
First google result for "php mixin": http://www.advogato.org/article/470.html
Short answer: yes, but not natively (yet, evidently, as #mchl notes). Check those out.
Longer answer: if you're using runkit, checkout runkit_method_copy(): "Copies a method from class to another."
I based mixins functionality on the blog entry found at jansch.nl.
class Node
{
protected $__decorator_lookup = array();
public function __construct($classes = array())
{
foreach($classes as $class)
if (class_exists($class))
{
$decorator = new $class($this);
$methods = get_class_methods($decorator);
if (is_array($methods))
foreach($methods as $method)
$this->__decorator_lookup[strtolower($method)] = $decorator;
}
else
trigger_error("Tried to inherit non-existant class", E_USER_ERROR);
}
public function __get($name)
{
switch($name)
{
default:
if ($this->__decorator_lookup[strtolower($name)])
return $this->__call($name);
}
}
public function __call($method, $args = array())
{
if(isset($this->__decorator_lookup[strtolower($method)]))
return call_user_func_array(array($this->__decorator_lookup[strtolower($method)], $method), $args);
else
trigger_error("Call to undefined method " . get_class($this) . "::$method()", E_USER_ERROR);
}
public function __clone()
{
$temp = $this->decorators;
$this->decorators = array();
foreach($temp as $decorator)
{
$new = clone($decorator);
$new->__self = $this;
$this->decorators[] = $new;
}
}
}
class Decorator
{
public $__self;
public function __construct($__self)
{
$this->__self = $__self;
}
public function &__get($key)
{
return $this->__self->$key;
}
public function __call($method, $arguments)
{
return call_user_func_array(array($this->__self, $method), $arguments);
}
public function __set($key, $value)
{
$this->__self->$key = $value;
}
}
class Pretty extends Decorator
{
public function A()
{
echo "a";
}
public function B()
{
$this->b = "b";
}
}
$a = new Node(array("Pretty"));
$a->A(); // outputs "a"
$a->B();
echo($a->b); // outputs "b"
EDIT:
As PHP clone is shallow, added __clone support.
Also, bear in mind that unset WON'T work (or at least I've not managed to make it work) within the mixin. So - doing something like unset($this->__self->someValue); won't unset the value on Node. Don't know why, as in theory it should work. Funny enough unset($this->__self->someValue); var_dump(isset($this->__self->someValue)); will produce correctly false, however accessing the value from Node scope (as Node->someValue) will still produce true. There's some strange voodoo there.

PHP get_called_class() alternative

I've got an Abstract PHP superclass, which contains code that needs to know which subclass its running under.
class Foo {
static function _get_class_name() {
return get_called_class();
//works in PHP 5.3.*, but not in PHP 5.2.*
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
}
class FooBar extends Foo {
}
Bar::other_code(); // i need 'Bar'
FooBar::other_code(); // i need 'FooBar'
This would work if I called the function get_called_class() -- however, this code is going to be run in PHP version 5.2.*, so that function is not available.
There's some custom PHP implementations of get_called_class() out there, but they all rely on going thru the debug_backtrack(), parsing a file name & line number, and running a regex (as the coder is not aware that PHP 5.2 has reflection) to find the class name. This code needs to be able to be run with php, ie. not only from a .php file. (It needs to work from a php -a shell, or an eval() statement.)
Ideally, a solution would work without requiring any code to be added to the subclasses… The only potential solution I can see though is adding the following code to each subclass, which is obviously a disgusting hack:
class FooBar extends Foo {
static function _get_class_name() {
return 'FooBar';
}
}
EDIT: Wait, this doesn't even seem to work. It would've been my last resort. Can anybody think of something similar to this solution that'd get me the required functionality. Ie., I'm willing to accept a solution that requires me to add one function or variable to each subclass telling it what its class name is. Unfortunately, it seems that calling self::_get_class_name() from the superclass calls the parent class' implementation, even if the subclass has overridden it.
In reality it is often helpful to know the actual called (sub)class when executing a superclass method, and I disagree that there's anything wrong with wanting to solve this problem.
Example, my objects need to know the class name, but what they do with that information is always the same and could be extracted into a superclass method IF I was able to get the called class name. Even the PHP team thought this was useful enough to include in php 5.3.
The correct and un-preachy answer, as far as I can tell, is that prior to 5.3, you have to either do something heinous (e.g. backtrace,) or just include duplicate code in each of the subclasses.
Working solution:
function getCalledClass(){
$arr = array();
$arrTraces = debug_backtrace();
foreach ($arrTraces as $arrTrace){
if(!array_key_exists("class", $arrTrace)) continue;
if(count($arr)==0) $arr[] = $arrTrace['class'];
else if(get_parent_class($arrTrace['class'])==end($arr)) $arr[] = $arrTrace['class'];
}
return end($arr);
}
This is not possible.
The concept of "called class" was introduced in PHP 5.3. This information was not tracked in previous versions.
As an ugly work-around, you could possibly use debug_backtrace to look into the call stack, but it's not equivalent. For instance, in PHP 5.3, using ClassName::method() doesn't forward the static call; you have no way to tell this with debug_backtrace. Also, debug_backtrace is relatively slow.
The PHP/5.2 alternative to late static binding that keeps duplicate code to the minimum while avoiding weird hacks would be to create one-liners on child classes that pass the class name as argument:
abstract class Transaction{
public $id;
public function __construct($id){
$this->id = $id;
}
protected static function getInstanceHelper($class_name, $id){
return new $class_name($id);
}
}
class Payment extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
class Refund extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
var_dump( Payment::getInstance(1), Refund::getInstance(2) );
object(Payment)#1 (1) {
["id"]=>
int(1)
}
object(Refund)#2 (1) {
["id"]=>
int(2)
}
The solution is:
get_class($this);
However, I don't know if this sentence works in static functions. Give it a try and tell me your feedback.
This hack includes the heinous use of debug_backtrace... not pretty, but it does the job:
<?php
function callerName($functionName=null)
{
$btArray = debug_backtrace();
$btIndex = count($btArray) - 1;
while($btIndex > -1)
{
if(!isset($btArray[$btIndex]['file']))
{
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
else
{
$lines = file($btArray[$btIndex]['file']);
$callerLine = $lines[$btArray[$btIndex]['line']-1];
if(!isset($functionName))
{
preg_match('/([a-zA-Z\_]+)::/',
$callerLine,
$matches);
}
else
{
preg_match('/([a-zA-Z\_]+)::'.$functionName.'/',
$callerLine,
$matches);
}
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
}
return $matches[1];
}
I have asked a question like this before, because I wanted a parent to have a factory method that was something like this
public static function factory() {
return new __CLASS__;
}
But it always returned the parent class, not the inherited one.
I was told that it is not possible without late static binding. It was introduced in PHP 5.3. You can read the documentation.
This function does the same job but works with instances too:
if (!function_exists('get_called_class')) {
function get_called_class() {
$bt = debug_backtrace();
/*
echo '<br><br>';
echo '<pre>';
print_r($bt);
echo '</pre>';
*/
if (self::$fl == $bt[1]['file'] . $bt[1]['line']) {
self::$i++;
} else {
self::$i = 0;
self::$fl = $bt[1]['file'] . $bt[1]['line'];
}
if ($bt[1]['type'] == '::') {
$lines = file($bt[1]['file']);
preg_match_all('/([a-zA-Z0-9\_]+)::' . $bt[1]['function'] . '/', $lines[$bt[1]['line'] - 1], $matches);
$result = $matches[1][self::$i];
} else if ($bt[1]['type'] == '->') {
$result = get_class($bt[1]['object']);
}
return $result;
}
}
<?php
class Foo {
private static $instance;
static function _get_class_name() {
return self::myNameIs();
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
public static function myNameIs() {
self::$instance = new Bar();
return get_class(self::$instance);
}
}
class FooBar extends Foo {
public static function myNameIs() {
self::$instance = new FooBar();
return get_class(self::$instance);
}
}
Bar::other_code(); // i need 'Bar'
FooBar::other_code(); // i need 'FooBar'

Categories