Calling method X from each parent - php

what i'm trying to do is call each method "init" from current class's parents.
I'm doing that to avoid programmers to have to call init method (parent::init()) each time they create an init method in a new controller.
Example:
class Aspic\Controller { } // main controller
class ControllerA extends Aspic\Controller { public function init() {/* do something 1 */}
class ControllerB extends ControllerA {}
class ControllerC extends ControllerB { public function init() { /* do something 2 */ }
class ControllerD extends ControllerC {}
As you can see the init methods do not call parent init method but i want my app (there is an option) do it.
Thus when I'm loading ControllerD, before calling it's init method (there isn't in the example but the app test it), i want to call each parent init method.
sound like this:
parent::init(); // Controller C init
parent::parent::parent::init(); // Controller A init
So i did :
if($this->_autoCallParentsInit) {
// Aspic\Controller is the main controller, which is the mother of all others
$aspicControllerRc = new \ReflectionClass('Aspic\\Controller');
$rc = new \ReflectionClass($this); // We are in D
$currPrefix = '';
// Calling each init methods of current class parent
// Avoid using parent::init() in each controller
while(($parentClass = $rc->getParentClass()) AND $aspicControllerRc->isInstance($parentClass)) {
/*
$aspicControllerRc->isInstance($parentClass)
=> because Aspic\Controller extends a "Base class". Thus, we stopped at Aspic\Controller
*/
$currPrefix .= 'parent::';
// Must have explicit method (not inherited from parent) BUT actually hasMethod does not care
if($parentClass->hasMethod('init')) {
call_user_func($currPrefix.'init');
}
}
}
This is not working because ReflectionClass::isInstance does not accept others argument than the object we want to test (and the not a ReflectionClass object representing it as in the example)
**
Simply:
I have an object $x, and i want to call the init method of each parent of the class of $x.
**
Is it possible ?
I hope i was clear :)
Thanks

ControllerB has an init() method by virtue of extending ControllerA, so you shouldn't have to call parent::parent::init() to get to A's from C. You should be fine to call parent::init() from ControllerD, which will call ControllerC's init() method. If ControllerC calls parent::init() it will be calling ControllerA's init() method.
If you're trying to skip the Controller's specific init() code when being called by a subclass, you could add a flag function init($call_parent = false) and then, from lower controllers, call parent::init(true);

If you're not using the classes statically (which, from your code not stating static function, I assume you're not), have you tried using the __construct() method? It gets automatically called when you instantiate the class, for example:
class MyClass {
public function __construct() {
echo 'Hello!';
}
}
$class = new MyClass();
That will automatically output 'Hello!', however if you extend the class and that child class contains a __construct() method you will have to put parent::__construct() inside the childs construct method, however you wont have to do it for every parent, just the once, for example:
class MyClassB extends MyClass {
public function __construct() {
parent::__construct();
echo 'World!';
}
}
class MyOtherClass extends MyClassB
public function __construct() {
parent::__construct();
echo 'How\'s it going!';
}
}
$class = new MyOtherClass();
That will output "Hello! World! How's it going!"

Related

php overriding method called by constructor in child class

I was playing around with a couple of classes to understand the relationship between parent and child. I setup the parent to have a constructor that calls an init method. Then when I add an init method to the child, it should override the parent init, shouldn't it? But what is happening is that both methods are being called.
To test this, I wrote a class named Model and a child called Instance. Here is the code:
$try = new Instance;
echo $try;
class Model{
public function __construct(){
$this->init();
}
public function init()
{
return $this->className();
}
public function __toString()
{
return $this->className();
}
public static function className()
{
return get_called_class();
}
}
class Instance extends Model
{
public function init()
{
echo "tada! ";
}
}
Gives the following output:
tada! Instance.
In class Model, I use the magic method __toString() to return the class name as a string. The constructor of the parent calls the parent init() method, which in this case echoes the class name.
My understanding is that if I write a child class, in this case the class is called Instance, with an init() method, it would overwrite the parent init() method, but that is not what is happening. In this case, it returns both init mehtods and I have no idea why. Can anyone explain this?
The fact is -
When you instantiate an object of Class "Instance" using "$try = new Instance;". it calls the constructor and since child class overrides "init" method, it prints "tada!".
On the other line you echo the object of Class "Instance" using "echo $try;" and a magic method __toString() implemented in parent class. So it print "Instance" as class name.
You can run only $try = new Instance; that will print only "tada!".
You are running two different commands. First you are instantiating the class with $try = new Instance; which is calling __construct. The __construct() in the parent class is calling init() which is using the child init() and returning tada!. Create a blank constructor in the child class to override the parent constructor.
The second command is when you echo the class: echo $try; using __toString() which is echoing Instance as defined in the parent class.
You can also add to a parent constructor by using:
public function __construct() {
...code...
parent::__construct();
...code...
}
Reason :
Since you don't have any constructor defined for your child class, the parent class constructor will be fired and that is why you get that output.
Explanation :
When you do
$try = new Instance;
The parent class constructor gets fired and it will call the init() which in turn calls the className() that does the get_called_class() , so eventually your init() in the child class will be called and you get the output "tada!" first.
And when you do..
echo $try;
The __toString() is called and returns you the class name Instance
So basically , If you had a constructor on your child class , you will not be getting the output "tada!"
In this case, it returns both init mehtods
No it doesn't;
'tada!' is being echoed by Instance::init() and 'Instance' is being echoed by Model::__toString().

How do I access a parent's methods inside a child's constructor in PHP?

Say I have class child() and class parent(). The parent has a constructor and a few other public methods, and the child is empty apart from a constructor.
How do I go about calling a parent's methods inside of the child's constructor, as in:
Class Parent {
public function __construct() {
// Do stuff (set up a db connection, for example)
}
public function run($someArgument) {
// Manipulation
return $modifiedArgument;
}
}
Class Child extends Parent {
public function __construct() {
// Access parent methods here?
}
}
Say I want to call parents run() method, do I have to call a new instance of the parent inside the child constructor? Like so...
$var = new Parent();
$var->run($someArgument);
If so, what is the point of extends from a class definition POV? I can call a new instance of another class with the new keyword whether it extends the 'child' or not.
My (likely) wrong understanding was that by using extends you can link classes and methods from a parent can be inherited into the child. Is that only outside the class definition? Does using extend offer no efficiencies inside the class definition?
Because referring to the parent's run() method with the this keyword certainly doesn't work...
Use parent as predefined reference: parent::run(). This will ensure you call parent method. The same way you could call first parent constructor first or after child one - parent::__construct().
Class Child extends Parent {
public function __construct() {
parent::__construct();
// Access parent methods here?
$some_arg = NULL; // init from constructor argument or somewhere else
parent::run($some_arg); // explicitly call parent method
// $this->run($some_arg); // implicitly will call parent if no child override
}
}
If you dont have an implementation in child you could call $this->run($args), where it will again call parent run method.
To extend Rolice's answer
function a() {
echo 'I exist everywhere';
}
class A {
protected $a
function a() {
$this->a = 'I have been called';
}
function out() {
echo $this->a;
a();
}
}
class B extends A {
function __construct() {
parent::a();// original method
$this->a(); // overridden method
a();
}
function a() {
$this->a = $this->a ? 'I have been overwritten' : 'first call';
}
}
Study these to understand the difference

Confused about abstract class in php

If I have an abstract class like this:
abstract class MyApp
{
public function init()
{
$this->stuff = $this->getStuff();
}
public function getStuff()
{
return new BlueStuff();
}
}
And then I have a class that extends from this abstract class like this:
class MyExtendedClass extends MyApp
{
public function init()
{
parent::init();
}
public function getStuff()
{
return new RedStuff();
}
}
If I do:
$myobj = new MyExtendedClass();
$myobj->init();
Why does the method getStuff from the child class get called? Isn't $this in the context of the abstract class? If so, shouldn't the method of the abstract class get called?
Thanks!
New answer
In PHP you can use subclasses as if all methods in the parent class that don't exist in the subclass have been copied to the subclass. So your example would be the same as:
class MyExtendedClass extends MyApp {
public function init() {
$this->stuff = $this->getStuff();
}
public function getStuff() {
return new RedStuff();
}
}
Just think of the subclass as having all code of the parent class and you're normally all right. There is one exception to this rule: properties. A private property of a class can only be accessed by that class, subclasses can't access the private properties of parent classes. In order to do that you need to change the private property into a protected property.
Original answer
Abstract classes in PHP are just like regular classes with one big difference: they can't get initiated. This means you can't do new AbstractClass();.
Since they work exactly like regular classes for everything else, this also is the case for extending classes. This means that PHP first tries to find the method in the initiated class, and only looks in the abstract classes if it doesn't exist.
So in your example this would mean that the getStuff() method from MyExtendedClass is called. Furthermore, this means you can leave out the init() method in MyExtendedClass.

Problem regarding use of constructors in PHP

My problem is in using Codeigniter custom library but I think it is not specific to that and more related to use of constructors in PHP.
I am trying to create a custom controller library in Codeigniter like this...
class MY_Controller extends Controller {
var $data = array();
function MY_Controller() {
parent::Controller();
$this->data['err'] = 'no';
$this->load->helper('utilities');
}
}
Now I create a codeigniter controller class like this...
class api_controller extends MY_Controller {
function get_data() {
$this->data['view'] = "data";
$this->load->view("data_view", $this->data);
}
function get_xml() {
$this->data['part'] = "xml";
$this->load->view("data_view", $this->data);
}
}
I want to ask that if the controller class extending the MY_Controller is instantiated when I access urls like api_controller/get_data and api_controller/get_xml, does the constructor of parent class always get called upon, i.e., MY_Controller() is always called.
Am I correct in inferring the following
get_data() called
-> $data => array ('err' => 'no', 'view' => 'data')
get_xml() called
-> $data => array ('err' => 'no', 'part' => 'xml')
Your code looks like it's using the PHP4 syntax for constructors. You should switch to the PHP5 syntax.
PHP4:
class MyClassName {
function MyClassName() {...} //constructor.
}
PHP5:
class MyClassName {
function __construct() {...} //constructor.
}
You can then call the constructor of a parent class by calling parent::__constructor(); from within the child class's __constructor() method:
class MyClassName extends SomeOtherClass {
function __construct() {
//...code here runs before the parent constructor.
parent::__construct();
//...code here runs after the parent constructor.
}
}
For PHP in general the parent constructor is not called default if the child has a constructor.
Constructors. It can be called using parent::_construct();
If you're using php 5+ you should go with the new recommended style of using function __construct() instead of the old style with a function named the same as a class.
As for CI-specific stuff I can't help you, sorry!
If you do not override __construct() in MY_Controller then Controller's __construct() will get run.
If you override it and then called parent::__construct() then it will run your own and the parent's.
So if you wanted it to run the parent's and your own you would do this
class MY_Controller extends Controller
{
var $data = array();
function __construct()
{
parent::__construct();
// Your code here
}
}
Yes, the parent constructor it is always called (you may want to rewrite them as __construct() however, thinking also at codeigniter 2.0 ).
If you really are in doubt toss in it an echo and see it for yourself.
Also the $this->data part is correct to me
You are right in your affirmations about the data array contents. In you code you wrote:
function MY_Controller() {
parent::Controller();
so the parents's class constructor is being called. There are lots of comments about PHP4 and PHP5 syntax, but basically everithing is ok.
In your question you wrote
that if the controller class extending the MY_Controller is instantiated
that is not correct. The instance is an object of class api_controller, calling the MY_Controller constructor is made using the same object. That is not the same, that is basic for polimorphism.
class Blog extends CI_Controller {
public function __construct()
{
parent::__construct();
// Your own constructor code
}
}

php oop extends problem

i have the following classes.
class base {
private function __construct(){
//check if foo method exists
//$this->foo(); // if it exists
}
public static function singleton(){
if(!isset(self::$singleton)){
self::$singleton = new base();
}
return self::$singleton;
}
}
class sub extends base{
public function __construct() {
parent::singleton();
}
public function foo(){
}
}
then init it like so
$test = new sub();
my problem is that I want to check on base __construct if the sub has a foo method.
but it doesn't seem to have this method.
can someone tell me where have I gone wrong?
Although you call parent::singleton() from sub class, but the singleton() still creates you instance of base class (because you do new base()), which does not have your foo() method.
In general you shouldn't use/call any methods from base class, which aren't define in it. Because it makes your code not clean: what if you will implement some another class which extends your base class, but forgets to implement the foo() method? You can end up with fatal errors quite fast...
If you are sure, that this foo() method will be always implemented by any child class - you can define in as abstract method in base class - then any child class will be forced to implement it. Or at least as an empty method in the same base class... This way your code will be clean and structured.
The method foo will not exist as the singleton being creating is an instance of base. Here's what's happening:
Create an instance of sub
Constructor of sub gets the singleton instance.
singleton() creates an instance of base
base constructor checks if the base class contains a method named foo. It does not.
Edit
i got a url e.g. site.com/sub/foo so the class sub is called and i want the base to check if foo does not exist then i will break the code... foo can be anything as long as it exists as a method on sub class
In this case the singleton pattern is superfluous. Instead, you can start with a basic factory class that implements the desired front controller pattern:
class factory {
static public function go($path) {
list($type, $action) = explode('/', $path);
if (class_exists($type) && // Does the requested type/class exist?
is_subclass_of($type, 'factory') && // Basic security. Make sure URL requests can't create an instance of *any* PHP class
method_exists($type, $action)) // Check requested method/action
{
$o = new $type;
$o->$action();
}
}
}
class sub extends factory {
public function foo() {
echo('To foo or not to foo that is the question.');
}
}
factory::go('sub/foo');

Categories