PHP: Can't set object property from within object method? - php

This should be so easy. I have a PHP class that has custom methods to return information about objects. I want to use the custom methods to determine whether or not a specific parameter has been set in the object. If it's set, return the property. If not, the method should contact the database to get the information and set the object's property.
However each time I run the code below, it appears that the object's property does not get set. The retrieve_user_first_name method hits the database access object every time.
I've tried checking the value of the property using is_null, empty, isset. I'm feeling like a dope.
require_once('dbaccess.class.php');
class User {
protected $user_id;
protected $first_name;
function __construct($user_id)
{
$this->user_id = $user_id;
}
function __get($variablename)
{
return $this->$variablename;
}
function __set($variablename, $variablevalue)
{
$this->$variablename = $variablevalue;
}
function __destruct()
{
}
function retrieve_user_first_name()
{
if(!isset($this->first_name)) {
$dbAccess = new DBAccess();
$strFirstName = $dbAccess->retrieve_user_first_name($this->user_id);
$this->first_name = $strFirstName;
}
else {
$strFirstName = $this->first_name;
}
return $strFirstName;
}
}

The code you posted works fine.
I think the problem is that you are not using the same object for your sequential calls in whatever code is accessing this object.
So something like:
$ob = new User();
$ob->retrieve_user_first_name();
$ob = new User();
print $ob->first_name;
is happening. Obviously, it's probably not so blatant, but that's the simplest explanation for your problem.
The only other option, given the code you posted, is that all of your database calls are returning empty strings or failing, so the variable is never getting updated. This is a very good possibility, so check both of these options in your code.

Related

Abstract function returning NULL instead of a string - PHP OOP

I have this SessionService.php file that handles session_start and session_regenerate_id functions.
I make use of an abstract class that generates a new session ID and stores it in a variable called $new_session_id. Then I return this variable as an argument of an abstract function called returnNewSessionID(), contained in the class SessionRegenerateID. This abstract function is designed to return the variable $new_session_id.
That's where the problem is. Instead of returning a string (the new session ID we generated), it returns NULL.
At createNewID(), you see I am assigning the newly generated code to a session variable called $_SESSION['new_session_id']. By echoing this session variable, I get the expected value from it. At the same time, echoing the returned value from the createNewID() function gives me NULL.
The ID is being generated. The problem most likely lies in the way this variable $new_session_id is being used inside these functions. I need to figure out why it is not being returned by the abstract function.
SessionService.php
interface SessionRegenerateInterface {
public function createNewID();
(... other unrelated functions ...)
}
abstract class AbstractSessionRegenerate implements SessionRegenerateInterface {
public function createNewID() {
$new_session_id = session_create_id();
$_SESSION['new_session_id'] = $new_session_id;
$this->returnNewSessionID($new_session_id);
}
abstract protected function returnNewSessionID($newID);
}
class SessionRegenerateID extends AbstractSessionRegenerate {
protected function returnNewSessionID($newID) {
return $newID;
}
}
SessionController.php
$RegenerateSession = new SessionRegenerateID;
$newID = $RegenerateSession->createNewID();
var_dump($newID); // outputs NULL
var_dump($_SESSION['new_session_id']); // outputs the generated session ID
I have an InputValidation class which is logically identical to this one, except for the type of value it returns: TRUE or FALSE. It works. But now that I am passing a variable with a string value, it returns NULL. I appreciate help to understand why.
Thanks to Jeto, I solved this by inserting a 'return' before $this->returnNewSessionID($new_session_id);
public function createNewID() {
$new_session_id = session_create_id();
$_SESSION['new_session_id'] = $new_session_id;
return $this->returnNewSessionID($new_session_id);
}

Is there a way to make __get & __set trigger for defined but empty properties in class

I´ve tried to understand if there is a magic function in php that can trigger a function when I try to access an object property like the following.
class User {
public $groups;
function loadGroups(){
$this->groups[] = "a list of groups";
}
function __get($name){
// Trigger on defined property such as groups
if($name == "groups"){
$this->loadGroups();
return $this->groups;
}
}
}
$user = new User();
foreach($user->groups as $group) // Is it possible to load $user->groups when it's accessed?
echo $group;
I do know that the __get & __set does not trigger if the property is defined in the class even tho the property is set to null or undefined, but is there any other way to trigger something for a defined property or do I have to create getters and setters for all these properties and make sure to always call these when i need to access the property and want to be sure that it's loaded when i access it? I do hope not due to It will result in many changes for the current system I'm working with.
I'm thankful for any information or help I can get to close this chapter.
// ZarToK
I get what you're trying to do, but I don't think you're asking the question right. You're obviously trying to do lazy loading.
There isn't a magic method for doing what you're requesting, but you can essentially create such a thing. There are two options: the "creating getters/setters" option - which is more in line with what a Java developer would do, or there's the "protect the variable and use magic method" option which tends to be "nice" for framework apis but can be a little confusing.
class User {
protected $groups;
private function loadGroups() {
$this->groups[] = 'a list of groups';
}
public function __get($name) {
if ($name == 'groups' && is_null($this->groups)) {
$this->loadGroups();
return $this->groups;
}
}
}
alternatively, the more Java way:
class User {
protected $groups;
private function loadGroups() {
$this->groups[] = 'a list of groups';
}
public function getGroups() {
if (is_null($this->groups)) {
$this->loadGroups();
}
return $this->groups;
}
}
__get() and __set() are used for reading/writing data from/to inaccessible properties. Inaccessible properties in this case would be private or protected, never public. If they need to be public then what is wrong with just returning from the function?
class User {
public $groups;
function loadGroups(){
$this->groups[] = "a list of groups";
return $this->groups;
}
}
$user = new User();
foreach($user->loadGroups() as $group)
echo $group;

Constructor returning value?

Looking at the following code, I see the constructor is returning a value. I thought that constructors only return objects. Can someone tell me what am I missing?
public function __construct($username = null, $password = null){
$urlLogin = "{$this->apiHost}/login/$username";
$postData = sprintf("api_type=json&user=%s&passwd=%s",
$username,
$password);
$response = $this->runCurl($urlLogin, $postData);
if (count($response->json->errors) > 0){
return "login error";
} else {
$this->modHash = $response->json->data->modhash;
$this->session = $response->json->data->cookie;
return $this->modHash;
}
}
Indeed you are correct. Nothing can be done with the return value of a constructor (aside from using the Object it created).
So no, you aren't missing anything, it's the developer who wrote that code who is.
It is technically possible to use return values from constructors, if you call the function directly
$obj->__construct();
That would allow you to use the constructor's return value. However, that is highly uncommon and fairly not recommended.
You can do whatever you want with the return value of a constructor, so it's not true that "Nothing can be done with the return value of a constructor (aside from using the Object it created)." The return value of a constructor is not the object "it" created. The constructor does not create objects (the new keyword does). The return value of a constructor is the same as that of any other function: whatever you choose to return. Further, it is also false that an object already has to exist in order to call its constructor. This is perfectly valid:
$parent_constructor_return_value = parent::__construct();
For example:
abstract class MyBase {
function __construct () {
return "Hello, world.";
}
}
class MyDerived extends MyBase {
function __construct () {
echo parent::__construct();
}
}
new MyDerived(); // prints "Hello, world."
While this is possible, I can't conceive of a scenario in which it would be best practice. After all, you could always call a method other than parent::__construct() to get your value, and all you lose is obscurity. I suppose it could be used as a way of error-handling--there are two other ways to accomplish the same thing:
Throw Exceptions in the parent constructor and catch them in your derived constructor.
Set properties in the parent constructor indicating that an error happened, and then check the state of those properties in the derived constructor.
If an error in a parent constructor is not exceptional, he might have decided to have the parent constructor return error values, rather than storing transient error information as object properties. Of course, then the only reason to name the parent's method __construct is if the parent class is not abstract but can itself be instantiated--but in that context, the returned error messages would never be seen. So, bad pattern; bad. Constructors are not intended to return values, which means you're opening an architectural can of worms by leveraging this mechanism.
A constructor returns nothing, but you can return from it (stopping the method execution at a point for some reason but the object can be created).
See this page: Returning a value in constructor function of a class
Read it:-
Constructors don't get return values; they serve entirely to instantiate the class.
Without restructuring what you are already doing, you may consider using an exception here.
public function __construct ($identifier = NULL)
{
$this->emailAddress = $identifier;
$this->loadUser();
}
private function loadUser ()
{
// try to load the user
if (/* not able to load user */) {
throw new Exception('Unable to load user using identifier: ' . $this->identifier);
}
}
Now, you can create a new user in this fashion.
try {
$user = new User('user#example.com');
} catch (Exception $e) {
// unable to create the user using that id, handle the exception
}
If you're always expecting a string as a return value you can add the method __toString() then if you try to print that class it will return what you placed in there, only strings, and i can see that it's your case here, so i believe that should work for you..
public function __construct($username = null, $password = null){
$urlLogin = "{$this->apiHost}/login/$username";
$postData = sprintf("api_type=json&user=%s&passwd=%s",
$username,
$password);
$response = $this->runCurl($urlLogin, $postData);
if (count($response->json->errors) > 0){
return "login error";
} else {
$this->modHash = $response->json->data->modhash;
$this->session = $response->json->data->cookie;
return $this->modHash;
}
}
public function __toString(){
return $this->modeHash;
}
...
echo yourClass($username, $password); // will return yourClass->modeHash;
Unlike in other languages, in PHP you can explicitly call the constructor. It's just another function. It looks like the original author first decided to put some code that could fail in the constructor, then realized that he needs a way to rerun the initialization after a failure.
$result = $user->__construct($username, $password)
would actually work and you do get the return value. It's an ugly way to do things obviously.
In my opinion, it's not a good practice to have code that trigger side effects in the constructor. I would put the code in a separate function, with a name that clearly states what it does.

Is it bad to return values from constructors?

Consider the following, simple class constructor. (note that I am obviously not including all methods that are referenced.
// Initialize User class.
public function __construct($user_id = NULL)
{
// If user is loaded (and a user ID is provided)
if ($user_id)
{
// If user is authorized.
if ($this->authorized($user_id))
{
// Load user information.
$this->info = $this->load($user_id);
}
else
{
// Return an empty (nonexistent) user.
return NULL;
}
}
// If user is loaded (and no user ID is provided)
else
{
// Create a new user.
$new_user = create_user();
// Return the new user's ID.
return $new_user;
}
}
My question is this: is my method of returning values here wrong? My friend insists that a constructor should ALWAYS return an object NO MATTER WHAT. However, the way I have it laid out here seems much simpler, and is much easier to work with. (If I am making a new user, then I get his ID right off the bat. If I am loading an existing user, I immediately have access to her/his information)
If it IS wrong, why? Why is this bad?
What you're trying to do simply doesn't work, the constructor will return the new instance of User anyway, even when you try to return null.
For example, this:
class User {
function __construct() {
return null;
}
}
var_dump(new User());
will print:
object(User)#1 (0) {
}
http://codepad.org/0IdJydkY
You could add a static method to your class to create the user or to return null
public static function createUser() {
// do your checks
// if valid return instance
// return null;
}
$user = User::createUser();
Note: You may have to make your authorized() method static - depends on the rest of your class.
Your __construct() function shouldn't return any value at all, it always automatically returns the object - it should be used to initiate certain things.
I would recommend putting the code in a different function.
More on this can be read here: Echo Return construct method;

When object property is asked first time gets data, and then just returns it - is it possible?

I need some data from the object.
I don't want these data to be loaded in class construction, because it is db heavy.
I don't want to load it more than once in a page.
I don't want to remember was it loaded already, or not.
$object->data // should be loaded in construction
$data = $object->get_data() // ok, but I need to remember was is got already, or not.
Is there a way to use $object->data, if it is asked first time, it actually gets data and returned it. And when I ask it after this, it just returns old data.
If there is no way, I will just use $data = $object->get_data(). But maybe I'm missing something.
This is usually solved using "lazy loading" - the property itself is backed using a private field, which gets initialized to some magic value (e.g. null) in the constructor, and gets filled the first time the getter gets called. After that, the getter returns the already-loaded value. Example:
class Foobar {
private $_lazy;
public function __construct() {
$this->_lazy = null;
}
public function __get($key) {
switch ($key) {
case 'lazy':
if ($this->_lazy === null)
$this->loadLazy();
return $this->_lazy;
}
}
private function loadLazy() {
$this->_lazy = rand();
}
}
Thing that you talking about is called Lazy Loading. You should implement that in method get_data(). If you wanna use it as property, not method, you must use PHP's magic __get method and return your data when accessing that data property.
But I recommend using method - it's more explict.
Well, you can do this
//create an object
class Foo{
//give some attributes
public $attr1;
public $attr2;
public $attr3;
public $attr4;
....
....
//create a function to load data
public function foofunction()
{
//and set the attrs
$this->attr1 = $somevalue;
$this->attr2 = $somevalue;
$this->attr3 = $somevalue;
//...
....
}
}
and you in your page
//create an object
$foo = new Foo();
//fetch data which will instantiate the attrs
$foo->foofunction();
//and you can use any attr at any time
echo $foo->attr1;
echo $foo->attr2;
//and this attr necessarily does not have to string, or int or ..
//it can be anything
Object has a property - flag, that indicates if the data have been asked before.
It's lazy loading
// simple implementation
public function get_data() {
if (is_null($this->_data)) {
$this->_data = $db->query();
}
return $this->_data;
}

Categories