Solved

calling the parent instead of child class

Posted on 2014-04-30
14
248 Views
Last Modified: 2014-05-09
<?php
abstract class ParamHandler {
    protected $source;
    protected $params = array();

    function __construct( $source ) {
        $this->source = $source;
    }

    function addParam( $key, $val ) {
        $this->params[$key] = $val;
    }

    function getAllParams() {
        return $this->params;
    }

    protected function openSource( $flag ) {
        $fh = @fopen( $this->source, $flag );
        if ( empty( $fh ) ) {
            throw new Exception( "could not open: $this->source!" );
        }
        return $fh;
    }

    static function getInstance( $filename ) {
        if ( preg_match( "/\.xml$/i", $filename )) {
            return new XmlParamHandler( $filename );
        }
        return new TextParamHandler( $filename );
    }

    abstract function write();
    abstract function read();
}

class XmlParamHandler extends ParamHandler {

    function write() {
        $fh = $this->openSource('w');
        fputs( $fh, "<params>\n" );
        foreach ( $this->params as $key=>$val ) {
            fputs( $fh, "\t<param>\n" );
            fputs( $fh, "\t\t<key>$key</key>\n" );
            fputs( $fh, "\t\t<val>$val</val>\n" );
            fputs( $fh, "\t</param>\n" );
        }
        fputs( $fh, "</params>\n" );
        fclose( $fh );
        return true;
    }

    function read() {
        $el = @simplexml_load_file( $this->source ); 
        if ( empty( $el ) ) { 
            throw new Exception( "could not parse $this->source" );
        } 
        foreach ( $el->param as $param ) {
            $this->params["$param->key"] = "$param->val";
        }
        return true;
    } 

}

class TextParamHandler extends ParamHandler {

    function write() {
        $fh = $this->openSource('w');
        foreach ( $this->params as $key=>$val ) {
            fputs( $fh, "$key:$val\n" );
        }
        fclose( $fh );
        return true;
    }

    function read() {
        $lines = file( $this->source );
        foreach ( $lines as $line ) {
            $line = trim( $line );
            list( $key, $val ) = explode( ':', $line );
            $this->params[$key]=$val;
        }
        return true;
    } 
}

class extendingParam extends ParamHandler{
      function getAllParams() {
        return $this->params;
    }
    function read(){
      parent::read();
    }
    function write(){
      parent::write();
    }
}

$file = "./texttest-fake.xmlfake"; 
//$file = "./texttest.txt"; 
$test = ParamHandler::getInstance( $file );
$test->addParam("key1", "val1" );
$test->addParam("key2", "val2" );
$test->addParam("key3", "val3" );
$test->write();

$test2 = ParamHandler::getInstance( $file );
$test2->read();

$arr2 = $test2->getAllParams();
print_r( $arr2 );

$test3=extendingParam::getInstance($file);
$test3->read();

$arr3= $test3->getAllParams();
print_r($arr3);

Open in new window



this code was originally from php book matt zandstra

I added

class extendingParam extends ParamHandler{

$test3=extendingParam::getInstance($file);
$test3->read();

$arr3= $test3->getAllParams();
print_r($arr3);

why does the code use
paramHandler::getAllParams
instead of
extendingParam::getAllParams
0
Comment
Question by:rgb192
  • 5
  • 4
  • 3
  • +2
14 Comments
 
LVL 34

Expert Comment

by:gr8gonzo
Comment Utility
I'm not 100% certain on this, because I -think- some types of object behavior has changed across different versions of PHP, but here is an educated guess:

Because extendingParam doesn't have its own static getInstance() method, it's going back to the parent to make the call, and so it's getting an instance of the parent instead of extendingParam.

I've noticed some funny behavior when it comes to inheriting static methods and properties. Unless the child specifically implements its own method of that same name, PHP will go back to the parent for the method/property value.
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
I don't know -- I don't see any reason in the code.  They seem to be the same thing.

ParamHandler::getAllParams() {
        return $this->params;
}

extendingParam extends ParamHandler{
    function getAllParams() {
        return $this->params;
    }
}
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
... when it comes to inheriting [strike]static[/strike] methods and properties. Unless the child specifically implements its own method of that same name, PHP will go back to the parent for the method/property value.
Wouldn't that be true for all methods and properties up the inheritance chain?
0
 
LVL 17

Expert Comment

by:jrm213jrm213
Comment Utility
how can you tell it is calling the Parent function when they both return the same thing?
0
 
LVL 51

Expert Comment

by:Julian Hansen
Comment Utility
Because you are using getInstance to get an Instance to the object and if you look at the code
    static function getInstance( $filename ) {
        if ( preg_match( "/\.xml$/i", $filename )) {
            return new XmlParamHandler( $filename );
        }
        return new TextParamHandler( $filename );
    }

Open in new window

getInstance only returns an instance of XmlParamHandler or TextParamHandler - nowhere in the code do you have logic to create an instance of class extendingParam

Don't get confused between the getInstance and the new operator.

The new operator will create an instance of the desired object.

The getInstance method is a static function (i.e. it can be called without first creating an instance of the object) and (in this case) this method checks the type of file being passed in to decide whether to create an XmlParamHandler or a TextParamHandler

If you want it to create your class you would have to add to the logic something like
    static function getInstance( $filename ) {
        if ( preg_match( "/\.xml$/i", $filename )) {
            return new XmlParamHandler( $filename );
        }
        if ( preg_match( "/\.exc$/i", $filename )) {
            return new extendingParam( $filename );
        }
        return new TextParamHandler( $filename );
    }

Open in new window

And invoke it like so
$file = 'test.exc';
$test4 = ParamHandler::getInstance( $file );
$arr4 = $test4->getAllParams();
print_r($arr4);

Open in new window

0
 
LVL 51

Expert Comment

by:Julian Hansen
Comment Utility
Following on from my previous post.

To further determine which of the methods is being called you can do the following.
In the parent change the definition of getAllParams to
function getAllParams() {
  echo "I am in the parent class";
  return $this->params;
}

Open in new window

And in your derived class change getAllParams to
function getAllParams() {
  echo "I am in the derived class";
  return $this->params;
}

Open in new window

When you invoke the getAllParams method you will be able to see which of the two was invoked
0
 

Author Comment

by:rgb192
Comment Utility
<?php
abstract class ParamHandler {
    protected $source;
    protected $params = array();

    function __construct( $source ) {
        $this->source = $source;
    }

    function addParam( $key, $val ) {
        $this->params[$key] = $val;
    }

    function getAllParams() {
        return $this->params;
    }

    protected function openSource( $flag ) {
        $fh = @fopen( $this->source, $flag );
        if ( empty( $fh ) ) {
            throw new Exception( "could not open: $this->source!" );
        }
        return $fh;
    }

    static function getInstance( $filename ) {
        if ( preg_match( "/\.xml$/i", $filename )) {
            return new XmlParamHandler( $filename );
        }
        return new TextParamHandler( $filename );
    }

    abstract function write();
    abstract function read();
}

class XmlParamHandler extends ParamHandler {

    function write() {
        $fh = $this->openSource('w');
        fputs( $fh, "<params>\n" );
        foreach ( $this->params as $key=>$val ) {
            fputs( $fh, "\t<param>\n" );
            fputs( $fh, "\t\t<key>$key</key>\n" );
            fputs( $fh, "\t\t<val>$val</val>\n" );
            fputs( $fh, "\t</param>\n" );
        }
        fputs( $fh, "</params>\n" );
        fclose( $fh );
        return true;
    }

    function read() {
        $el = @simplexml_load_file( $this->source ); 
        if ( empty( $el ) ) { 
            throw new Exception( "could not parse $this->source" );
        } 
        foreach ( $el->param as $param ) {
            $this->params["$param->key"] = "$param->val";
        }
        return true;
    } 

}

class TextParamHandler extends ParamHandler {

    function write() {
        $fh = $this->openSource('w');
        foreach ( $this->params as $key=>$val ) {
            fputs( $fh, "$key:$val\n" );
        }
        fclose( $fh );
        return true;
    }

    function read() {
        $lines = file( $this->source );
        foreach ( $lines as $line ) {
            $line = trim( $line );
            list( $key, $val ) = explode( ':', $line );
            $this->params[$key]=$val;
        }
        return true;
    } 
}

class extendingParam extends ParamHandler{
      function getAllParams() {
        return $this->params;
    }
    function read(){
      //parent::read();
      echo '<br>'.__METHOD__.'<br>';
    }
    function write(){
      parent::write();
    }
    static function getInstance( $filename ) {
        if ( preg_match( "/\.xml$/i", $filename )) {
            return new XmlParamHandler( $filename );
        }
        if ( preg_match( "/\.exc$/i", $filename )) {
            return new extendingParam( $filename );
        }
        return new TextParamHandler( $filename );
    }    
}

$file = "./texttest-fake.xmlfake"; 
//$file = "./texttest.txt"; 
$test = ParamHandler::getInstance( $file );
$test->addParam("key1", "val1" );
$test->addParam("key2", "val2" );
$test->addParam("key3", "val3" );
$test->write();

$test2 = ParamHandler::getInstance( $file );
$test2->read();

$arr2 = $test2->getAllParams();
print_r( $arr2 );

$test3=extendingParam::getInstance($file);
$test3->read();

$arr3= $test3->getAllParams();
print_r($arr3);

Open in new window


since adding new method
extendingParam::getInstance

$test3=extendingParam::getInstance($file);
extendingParam::getInstance is called



$test3->read();

something magical that I do not understand happens in
extendingParam::getInstance

and


textparamhandler::read() is called


extendingParam::read() is not called
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
Taken together, this just makes no sense and should probably throw a fatal error. Here's why I think this is off-course.

Line 2:
abstract class ParamHandler

Line 33 and 34 inside abstract class ParamHandler:
abstract function write();
abstract function read();

Line 88:
class extendingParam extends ParamHandler

Now at this point we have established that read() and write() methods of ParamHandler are abstract, meaning that ParamHandler cannot be instantiated and any class that extends ParamHandler must provide a definition for the read() and write() methods.  But...

Line 96-98:
function write(){
   parent::write();
}

By this definition, when write() is called on the concrete instance of extendingParam, a call is made to the abstract method().  I don't see how this can end well.
0
 
LVL 51

Expert Comment

by:Julian Hansen
Comment Utility
textparamhandler::read() is called
extendingParam::read() is not called

That is because you are calling the static getInstance method on the extendingParam class.
$test3=extendingParam::getInstance($file);

Open in new window

Lets look at what that does. ($filename = "./texttest-fake.xmlfake" by line 110 of your source listing)
static function getInstance( $filename ) {

        // THIS IS NOT GOING TO MATCH BECAUSE THE REGEX IS
        // LOOKING FOR A STRING THAT ENDS .xml AND 
        // $filename ENDS .xmlfake
        if ( preg_match( "/\.xml$/i", $filename )) {
            return new XmlParamHandler( $filename );
        }
        // THIS IS ALSO NOT GOING TO MATCH
        if ( preg_match( "/\.exc$/i", $filename )) {
            return new extendingParam( $filename );
        }

        // SO THIS IS WHAT EXECUTES
        // YOU ARE RETURNING AN OBJECT OF TYPE
        // TextParamHandler
        return new TextParamHandler( $filename );
    }    

Open in new window

Therefore when you invoke the method
$test3->read()

Open in new window

You are going to be calling the read method defined in the TextParamHandler class and not the one defined in extendingParam.

Try changing
$filename = "./texttest-fake.exc";

Open in new window

and see what happens.
0
 

Author Comment

by:rgb192
Comment Utility
$filename = "./texttest-fake.exc";

now
extendingParam::read() is called


so now my question:
how can
$test3=extendingParam::getInstance($file);
return an object


            return new XmlParamHandler( $filename );
   
            return new extendingParam( $filename );
       
        return new TextParamHandler( $filename );
   

because when object is returned the next method calls change based upon the object that was called

$test3->read();
$test3->getAllParams();
---------------------------------------------------------------------------------



I do not know if Ray's comments reflect Julian's method
http://www.experts-exchange.com/viewCodeSnippet.jsp?refID=40042672&rtid=20&icsi=2

and/or I do not understand the changes I should make
0
 
LVL 51

Expert Comment

by:Julian Hansen
Comment Utility
how can
$test3=extendingParam::getInstance($file);
return an object
Because that is how the class hierarchy has been defined. The following example should explain this more clearly

Define a parent class
Class parent 
{
    function read()
    {
        echo "parent read";
    }

    function another_function()
    {
        echo "parent another function";
    }
}

Open in new window

Now define two child classes in each case override the read() function but only in the second extend the another_function()
class firstChild extends parent
{
    function read()
    {
        echo "firstChild read overrides parent";
    }
}

class secondChild extends parent
{
    function read()
    {
        echo "secondChild read overrids parent";
    }
    
    function another_function()
    {
        parent::another_function();
        echo "secondChild another_function extends parent";
    }
}
// BLOCK A
$parent = new parent();
$parent->read();
$parent->another_function();
// Output
// parent read
// parent another function

// BLOCK B
$firstChild = new firstChild();
$firstChild->read();
$firstChild->another_function();
// Output
// firstChild read overrides parent
// parent another function

// BLOCK C
$secondChild = new secondChild();
$secondChild->read();
$secondChild->another_function();
// Output
// secondChild read overrides parent
// parent another function
// secondChild another function extends parent

Open in new window

If we look at the first block (BLOCK A) where we create a new parent object. When we call read() and another_function() the interpreter starts at the parent class and looks to see if this class defines those two methods - which it does so it invokes those

If we look at the second block (BLOCK B) where we create a new firstChild object. When we call read() the interpreter finds a definition of read in the firstChild class - so it invokes that. However when we call another_function there is no definition for another_function in the firstChild class so the interpreter goes one level up to firstChild's parent to see if it can find a definition of the function there. The interpreter will stop traversing the hierarchy as soon as it finds a matching definition of the called method.

In the case of the read() function for firstChild and secondChild - you will notice that the parent read() is not callsed - the read() methods in the child classes override the functionality in the parent. To invoke the parent functionality you would have to specifically call the method in question from within the method local to the class. This is demonstrated in the secondChild another_function()

If we look at the third block (BLOCK C) where we create a new secondChild object. The same outcome for read will occur as for firstChild. The read() method defined in the secondChild class will be invoked. When the another_function method is called on secondChild a definition for another_function exists in the secondChild class - but you will notice the first thing this function does is to invoke the parent method before continuing to do its output. In this scenario the class has extended the functionality of another_function by calling the parent version and then adding to it.

To answer you question (if I understand what you are asking)
because when object is returned the next method calls change based upon the object that was called
This will happen because of the way the parent and child have been setup - if you are referring to the getAllParams() call - then you will notice this is defined in the parent ParamHandler class and it is defined in the extendingParam class but not in the TextParamHandler class or the XmlParamHandler class.

So when you invoke the getAllParams() method on an object of either TextParamHandler or XmlParamHandler - this method is not defined in these classes so the parent method will be invoked.

In the case of the ParamHandler class - a getAllParams() method does exist in this class so it overrides the parent.

Does that answer your question
0
 

Author Comment

by:rgb192
Comment Utility
<?php
Class parent1 
{
    function read()
    {
        echo "parent read";
    }

    function another_function()
    {
        echo "parent another function";
    }
}


class firstChild extends parent1
{
    function read()
    {
        echo "firstChild read overrides parent";
    }
}

class secondChild extends parent1
{
    function read()
    {
        echo "secondChild read overrids parent";
    }
    
    function another_function()
    {
        parent::another_function();
        echo "secondChild another_function extends parent";
    }
}
// BLOCK A
$parent = new parent1();
$parent->read();
$parent->another_function();
// Output
// parent read
// parent another function

// BLOCK B
$firstChild = new firstChild();
$firstChild->read();
$firstChild->another_function();
// Output
// firstChild read overrides parent
// parent another function

// BLOCK C
$secondChild = new secondChild();
$secondChild->read();
$secondChild->another_function();
// Output
// secondChild read overrides parent
// parent another function
// secondChild another function extends parent

Open in new window


I understand method overriding in this basic example
but do not understand complex examples where an object is a return


Don't get confused between the getInstance and the new operator.

The new operator will create an instance of the desired object.

could you give an example because I only have one example: code sample from original question.
0
 
LVL 51

Accepted Solution

by:
Julian Hansen earned 500 total points
Comment Utility
Going back to your question
how can
$test3=extendingParam::getInstance($file);
return an object
A function or method can return anything. In this case the getInstance method returns an object. The important thing is that it can return different types of object based on the extension of the file name given.

I can create a function called factory like this
function & factory($type=null)
{
    $obj = null;
    switch($type) {
        case 'XML':
            $obj = new XMLClass();
        break;
        case 'TXT':
            $obj = new TextClass();
        break;
        default:
            $obj = new DefaultClass();
        break;
    }
    return $obj;
}
$val = 'XML';
$test = factory($val); // Returns an object of type XMLClass();
$val = 'TXT';
$test2 = factory($val); // Returns an object of type TextClass();
$test3 = factory(); // Returns an object of type DefaultClass();

Open in new window

We could achieve the same outcome by doing this
if ($val == 'XML') {
  $test = new XMLClass();
}
if ($val == 'TXT') {
    $test2 = new TextClass();
}
if ($val == '') {
    $test3 = new DefaultClass();
}

Open in new window

While not identical in function the above demonstrates that it would be much more elegant to have a method that decides what object to create than for us to have to use the if statements each time we needed to create a class of type XMLClass, TextClass or DefaultClass.

The function returns the new object effectively in the same way the new operator does - the effect on the left hand variable is the same - it is set to an object.
0
 

Author Closing Comment

by:rgb192
Comment Utility
getInstance method returns an object. The important thing is that it can return different types of object based on the extension of the file name given


thanks


Also I have a followup question asking for more examples of an instance of a class created inside a class
http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28430190.html
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Deprecated and Headed for the Dustbin By now, you have probably heard that some PHP features, while convenient, can also cause PHP security problems.  This article discusses one of those, called register_globals.  It is a thing you do not want.  …
This article discusses four methods for overlaying images in a container on a web page
The viewer will learn how to count occurrences of each item in an array.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now