Basic PHP question about classes

Look at this short code snippet:

class User {
    private $_first_name = "John";
    
    public function get_first_name() {
        return $this->_first_name;
    }
    
    public function set_first_name($value) {
        $this->_first_name;
    }
}

$u = new User();
$u->first_name = "Jane"; // This is somehow required. Without this, the next line will get an "Undefined property" error. Why?
echo $u->first_name;

Open in new window

elepilAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Dave BaldwinFixer of ProblemsCommented:
You forgot the ().  Here you go:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<title>PHP OOP Class test</title>
</head>
<body>
<h1>PHP OOP Class test</h1>
<?php 
class User {
    private $_first_name = "John";
    
    public function get_first_name() {
        return $this->_first_name;
    }
    
    public function set_first_name($value) {
        $this->_first_name = $value;
    }
}

$u = new User();
//$u->first_name = "Jane"; // This is somehow required. Without this, the next line will get an "Undefined property" error. Why?
echo $u->get_first_name();
$u->set_first_name('Elizabeth');
echo '<br>';
echo $u->get_first_name();

 ?>
</body>
</html>

Open in new window

0
Ray PaseurCommented:
It looks to me like the script sets the $_first_name property to "private" which means it cannot be accessed by any code outside of the class.  As a result, this should be expected to cause a fatal error:

$u->first_name = "Jane";

For that matter, so would this:

echo $u->first_name;

Make the property "public" and the script will work just fine.  PHP has a reasonably good object oriented programming model, documented here: http://php.net/manual/en/language.oop5.php

Give yourself some time to work with examples and practice using this new way of thinking about software development.  It's not as easy as it looks, and it's wa-a-a-y more powerful than anything you can write in procedural code!
0
Ray PaseurCommented:
Afterthought... You can use var_dump($u) to show the properties and their scopes and values.
0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

elepilAuthor Commented:
To David Baldwin. My sample was using getters/setters, I didn't need the parenthesis. So my get_first_name() and set_first_name() functions were usable as simply first_name without parenthesis as I showed in my sample.

But you didn't answer my question, I was asking why, despite my having initialized $_first_name inside the class to "John", I couldn't access it and kept getting an Undefined property error.
0
elepilAuthor Commented:
To Ray Paseur. My declaring $_first_name private was by design, because I don't want it accessible directly outside of the class. That's why when you look at get_first_name(), you can see I'm returning $this->_first_name. This is the classic OOP of doing things.

I still don't have an explanation to my question though. I initialized $_first_name to "John". Upon instantiation of the class, I was assuming $_first_name should've already had the value "John", which I was trying to return in the get_first_name() getter function. But I still don't understand why PHP is not allowing that.
0
Dave BaldwinFixer of ProblemsCommented:
Your code worked as soon as I commented out the first line and added () to the second one.  So I'm inclined to disagree with you.  In addition, your code used '$u->first_name' and not '$u->_first_name'... which would not have worked with the 'class' since that is a 'private' variable.
0
Chris StanyonWebDevCommented:
Your class has one private property and 2 public methods. That's it. Because your property is private, you can't directly access it. You only have access to the 2 public methods, and methods have to be called with the parenthesis.

You're instantiating your private property, but you can only access it by calling the public methods. Your setter is wrong - currently it does nothing - you need to assign the argument to the private proeprty:

public function set_first_name($value) {
        $this->_first_name = $value;
}

Open in new window


$u = new User();
$u->first_name = "Jane"; // you can't do this because $first_name doesn't exist!
$us->_first_name = "Jane; // you can't do this because $_first_name is private
echo $u->first_name; // you can't do this because, again, $first_name doesn't exist
echo $u->get_first_name(); // you can do this, but it's a method, so must include the parenthesis. By default it will return 'John'
$u->set_first_name('Jane'); // you can do this but you're not directly setting a property - you're calling a method

Open in new window

0
Brian TaoSenior Business Solutions ConsultantCommented:
1) in PHP, setter and getter do not work as you imagine... ^^.  You either implement __get and __set methods or you have to call the public functions by using something like $u->get_first_name();

2) Your $u->first_name; is a different property than the private $_first_name.  When you commented out the first line, no such property is defined before you tried to access it, so it says undefined.  When you add the assignment line, PHP couldn't find the property and "magically" adds a new public $first_name property for you, thus it became defined in the next line.  You can prove it by echoing both $u->first_name; (saying Jane) and $u->get_first_name(); (still says John).
0
elepilAuthor Commented:
To Brian Tao.

You said, "You either implement __get and __set methods or you have to call the public functions by using something like $u->get_first_name();"

Brian, it is to my understanding that I am implementing a pair of getter/setter methods. Correct me if I'm wrong, but to create a getter and setter, one would precede the function name with get_ and set_, respectively, is that not correct? You seem to be suggesting a different syntax (i.e., __get and __set). If I am not doing it right, can you show me how you would create a getter/setter function within a class?
0
elepilAuthor Commented:
To Chris Stanyon. You are right, my setter is wrong, my mistake, can't believe that slipped past me. It should really be:

public function set_first_name($value) {
    $this->_first_name = $value;
}

Open in new window


but that wasn't the reason for my posting. My primary reason for posting is why, despite my having initialized $_first_name as "John", it wasn't being returned in my getter function get_first_name() the value of $_first_name.

It's a mystery to me why none of you responders noticed I was implementing a getter/setter pair of functions for the property first_name. Maybe that's what I'm not doing right? Please tell me.

I wasn't trying to access the private variable $_first_name directly from outside the class, I was trying to get its value via the getter function (i.e., get_first_name() function) which returns $_first_name. Based on my reading, I then should be able to access the public getter method as:

$u->first_name; // Notice, no parenthesis

All responders seem to even be accommodating calling get_first_name() and set_first_name(), which would counter everything I've learned about PHP getter/setter functions. :(
0
elepilAuthor Commented:
To David Baldwin:

Are you aware I was using a getter/setter function pair to access the first_name property from within the User class?

Unless I'm not defining my getter function correctly, which is to precede the function name with get_, under no circumstances am I supposed to call the function get_first_name() at all because it's not meant to be used that way, which was what you were telling me to do.
0
Dave BaldwinFixer of ProblemsCommented:
I can't find any use of get_ in the PHP man pages.  This page on the OOP basics http://php.net/manual/en/language.oop5.basic.php shows using () to call public functions.  Maybe this page http://php.net/manual/en/language.oop5.overloading.php#object.get is what you were thinking of.  But it clearly shows that you have to create a __get or __set function and that it is not otherwise automatic.
0
elepilAuthor Commented:
To Dave Baldwin.

That was what another guy implied when he responded to another post I made. But from what I read, __get and __set are not really substitutes for getters/setters.

I am new to PHP, and learning it has been a frustrating experience for me. From my google searches on this subject matter, I have seen many pages that demonstrate get_ and set_ prefixes on function names to effect a getter/setter, but they don't work. It's almost as if get_/set_ was working at one time, and then just ceased to work with no trace at all in the PHP online manual, not even a mention that it might have worked at one time with an older PHP version.

You know what I'm trying to accomplish, Dave. How would you create a getter/setter in PHP so that you can get/set properties this way:

echo $className->propertyName; // to get the property value
$className->propertyName = "someValue"; // to set the property value

And by the way, I know you can just create a public variable inside the class, but that's not what getters/setters are all about. Is there even a way to do that in PHP?
0
Dave BaldwinFixer of ProblemsCommented:
The __get / __set page above clearly shows how to do that.  But nowhere can I find a page that describes the use of 'get_/set_'.  Not even the old PHP4 OOP shows that.  Do you have a link to a page that describes 'get_/set_'?
0
Chris StanyonWebDevCommented:
Right elepil,

Starting to understand where the confusion is coming in.

Preceding a property name with get_ and set_ does not magically give you access to that property - you're simply creating public functions that can get and set a private variable. A function called get_first_name will not automatically get fired when you call $u->first_name - that's just not how PHP works. You could just as easily call your getter and setter methods as getMyFirstName() or setMyFirstName(). There's no automatic association to a private property.

It's possible that you are confusing your manual getter / setter functions with PHPs magic methods __get and __set. These methods will automatically get fired for getting and setting private properties. So this would work:

class User {
	private $first_name = "John";

	public function __get($property) {
		if (property_exists($this, $property)) {
			return $this->$property;
		}
	}

	public function __set($property, $value) {
		if (property_exists($this, $property)) {
			$this->$property = $value;
		}
	}
}

$u = new User();
echo $u->first_name;

Open in new window


And as for your $_first_name proeprty not be returned by your get_first_name() method - it worked fine for me!
0
Brian TaoSenior Business Solutions ConsultantCommented:
To have your code work, add the following 2 methods (no need to change anything else) to your class:
    public function __get($property) {
      if (property_exists($this, $property)) {
        return $this->$property;
      }elseif (method_exists($this, "get_$property")){
        return call_user_func_array(array($this, "get_$property"), array());
      }
    }

    public function __set($property, $value) {
      if (property_exists($this, $property)) {
        $this->$property = $value;
      }elseif (method_exists($this, "set_$property")){
        return call_user_func_array(array($this, "set_$property"), array($value));
      }
    }

Open in new window


So the entire class will be like this:
class User {
    private $_first_name = "John";
    
    public function __get($property) {
      if (property_exists($this, $property)) {
        return $this->$property;
      }elseif (method_exists($this, "get_$property")){
        return call_user_func_array(array($this, "get_$property"), array());
      }
    }

    public function __set($property, $value) {
      if (property_exists($this, $property)) {
        $this->$property = $value;
      }elseif (method_exists($this, "set_$property")){
        return call_user_func_array(array($this, "set_$property"), array($value));
      }
    }
    
    public function get_first_name() {
        return $this->_first_name;
    }
    
    public function set_first_name($value) {
        $this->_first_name = $value;
    }
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Ray PaseurCommented:
Notwithstanding earlier comments, this is not really a question about object-oriented programming.  It seems to be question about confusing variable names, a mistake we all make at one time or another.  

The object property is named with a leading underscore.

private $_first_name

The script injects a different variable name.

$u->first_name = "Jane";

This is the sort of thing you see immediately if you use var_dump().  Var_dump() is your friend, always!  See the output of this script to learn what happened.
http://iconoun.com/demo/temp_elepil.php

<?php

/**
 * See http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28636010.html
 */
error_reporting(E_ALL);
echo '<pre>';

class User {
    private $_first_name = "John";

    public function get_first_name() {
        return $this->_first_name;
    }

    public function set_first_name($value) {
        $this->_first_name;
    }
}

$u = new User();

echo PHP_EOL . "BEFORE INJECTING A PROPERTY INTO THE OBJECT: ";
var_dump($u);

$u->first_name = "Jane";
echo $u->first_name;

echo PHP_EOL . "<b>AFTER</b> INJECTING A PROPERTY INTO THE OBJECT: ";
var_dump($u);

Open in new window

0
Ray PaseurCommented:
Footnote: If you're not sure about the scope of visibility, make everything public.  Then once the application works, go back to it and ask yourself if there is any advantage to making some of the properties or methods less visible.  When you have invisible properties, you have to dump the entire object to see any part of it - not nearly as much fun as it sounds, and of little utility if you have xdebug installed.  And when you have a private property or method, you can't extend the class with respect to the private parts.  In all my years of programming (outside of purely academic exercises) I have never seen a good utility for private elements.  Properly designed class encapsulation is really all you need unless you've surrounded yourself with untrustworthy programmers, and in that case OOP encapsulation may not be your biggest problem :)

Why public?  Well, for one thing you may want to use Reflection and automated testing.  Automated test tools need access to the properties and methods of your classes, so they can replace them in mock-object testing strategies.  If you've got a protected method, the only classes that can see the method are the ones that extend your concrete class, and believe me, you don't want to rewrite PHPUnit just to interface with your one class because you marked a method less visible than public.

Early on in my experiments with OOP encapsulation I found myself experimenting with reduced visibility.  Today, I don't worry about that any more -- it's one less thing to think about and it removes a stumbling block from my application development process.
0
elepilAuthor Commented:
Three responders came up with the best solution, but I tagged Brian Tao's solution as "Best Solution" only because he was the first to really see my problem.

Thanks to all responders!
0
Brian TaoSenior Business Solutions ConsultantCommented:
Thanks for the points.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
PHP

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.