I'm running the following code:
class Foo {
private $var = 0;
function isVarSet () {
return ($this->var != 0);
}
}
...
foo = new Foo();
results in an "undefined property" notice: foo::$var on my PHP (ver. 5.3.5).
if I rewrite just the function isVarSet():
function isVarSet() {
if (isset($this->var))
return ($this->var != 0);
return false;
}
the notice disappears.
This I do not understand. $var is set in both cases, why would it be an undefined property? Why do I need to use isset() to prevent this notice? Also, why does the notice refer to $var with the scope operator :: ? I'm not using a static class, I'm using an instance foo. $foo->isVarSet() should access a $var that is both defined and non-static.
I've been working on this for hours now and read all other answers on the undefined property notice, but this one I just don't get. Please enlighten me, StackOverFlow masters.
the code in my application:
<?php
class session {
private $userId = 0;
function __construct() {
session_start();
$this->setUserId();
}
public function isLoggedIn() {
//if (isset($this->userId))
return ($this->userId != 0);
//return false;
}
function getUserId() {
if (isset($this->userId))
return $this->userId;
else
return false;
}
private function setUserId() {
if (isset($_SESSION['userId'])) {
$this->userId = $_SESSION['userId'];
} else
unset($this->userId);
}
public function login($user) {
if ($user != null) {
$_SESSION['userId'] = $user->id;
$this->userId = $user->id;
}
}
public function logout() {
unset($_SESSION['userId']);
unset($this->userId);
}
}
$session = new Session();
?>
The call to the session class is made like so:
if ($session->isLoggedIn())
redirectToLocation("../public/index.php");
(after the whole edit).
What do you think this line does (in setUserId):
unset($this->userId);
You might just want to set it to 0 as previous (which you recognize as not logged in:
$this->userId = 0;
Or:
$this->userId = null;
Take your pick.
php 5.3.5/windows
<?php
function eh($errno, $errstr) {
echo "[$errno] $errstr";
}
set_error_handler('eh');
class Foo {
private $var = 0;
function isVarSet () {
return ($this->var != 0);
}
public function testVar() {
return var_export($this->isVarSet());
}
}
$foo = new Foo();
echo $foo->testVar();
?>
output is:
false
for $var=1, output is:
true
So it works perfectly here.
I think it is because you didn't initialize your variable in your first example. You need to first initialize the var in a method which is called. Only then it is initialized.
Hope this helps you.
Related
So I am facing this problem. I have a class representing a record in my database (User in this example). The class has as many properties as the database table has columns. For simplicity, I have just three in my example:
$id - ID of the user (must be set to a positive integer for registered user, might be set to 0 for user objects that aren't saved in the database yet)
$name - Name of user (must be set for every user, but before loading it from the database might be undefined)
$email - E-mail address of the user (might be NULL in case the user didn't submit an e-mail address)
My (simplified) class looks like this:
<?php
class User
{
private $id;
private $name;
private $email;
public function __construct(int $id = 0)
{
if (!empty($id)){ $this->id = $id; }
//If $id === 0, it means that the record represented by this instance isn't saved in the database yet and the property will be filled after calling the save() method
}
public function initialize(string $name = '', $email = '')
{
//If any of the parameters isn't specified, prevent overwriting curent values
if ($name === ''){ $name = $this->name; }
if ($email === ''){ $email = $this->email; }
$this->name = $name;
$this->email = $email;
}
public function load()
{
if (!empty($this->id))
{
//Load name and e-mail from the database and save them into properties
}
}
public function save()
{
if (!empty($this->id))
{
//Update existing user record in the database
}
else
{
//Insert a new record into the table and set $this->id to the ID of the last inserted row
}
}
public function isFullyLoaded()
{
$properties = get_object_vars($this);
foreach ($properties as $property)
{
if (!isset($property)){ return false; } //TODO - REPLACE isset() WITH SOMETHING ELSE
}
return true;
}
//Getters like getName() and getId() would come here
}
Now finally to my problem. As you can see, the instance of this class can be created without all properties set. That's a problem in case I want to e. g. call getName() while the name isn't known yet (it wasn't set via the initialize() method and load() wasn't called). For that, I wrote method isFullyLoaded() which checks if all properties are known and if not, load() should be called (from the method calling isFullyLoaded(). And the core of the problem is, that some variables might be empty strings (''), zero values (0) or even null (like the $email property). So I want to distinguish variables that have any value set (including null) and those who have never been assigned any value.
Specific example: I want to achieve this code:
$user1 = new User(1);
$user1->initialize('Vic', 'nerd.from.klamath#fallout2.com');
var_dump($user1->isFullyLoaded());
$user2 = new User(2);
$user2->initialize('Cassidy', null); //No e-mail was specified during the registration
var_dump($user2->isFullyLoaded());
$user3 = new User(3);
$user3->initialize('Myron'); //E-mail isn't known yet, but might be saved in the database
var_dump($user3->isFullyLoaded());
to output this:
bool(true)
bool(true)
bool(false)
TL:DR How do distinguish undefined variable and variable which has been assigned NULL in PHP?
Here is another way to introduce the custom Undefined class (as singleton). Additionally, be sure that your class properties are typed:
class Undefined
{
private static Undefined $instance;
protected function __constructor()
{
}
protected function __clone()
{
}
public function __wakeup()
{
throw new Exception("Not allowed for a singleton.");
}
static function getInstance(): Undefined
{
return self::$instance ?? (self::$instance = new static());
}
}
class Person
{
private int $age;
public function getAge(): int|Undefined
{
return $this->age ?? Undefined::getInstance();
}
}
$person = new Person();
if ($person->getAge() instanceof Undefined) {
// do something
}
But there is a downside with using singleton pattern, because all the undefined objects in your app will be strictly equal to each other. Otherwise, every get operation returning undefined value will have a side effect namely another piece of allocated RAM.
PHP has not the value undefined like javascript. But it is not strict typed so if you do not find a better solution here is one with an custom type UNDEFINED
<?php
class UNDEFINED { }
class Test {
var $a;
function __construct( $a='' ) {
$this->a = new UNDEFINED();
if( $a !== '' ) {
$this->a = $a;
}
}
function isDefined() {
$result =true;
if(gettype($this->a) === 'object'){
if(get_class($this->a) === 'UNDEFINED') {
$result=false;
}
}
echo gettype($this->a) . get_class($this->a);
return $result;
}
}
$test= new Test();
$test->isDefined();
Here is a may be litte better version which used instanceof instead of get_call and getType
<?php
class UNDEFINED { }
class Test {
var $id;
var $a;
var $b;
function __construct( $id) {
$this->id = $id;
$this->a = new UNDEFINED();
$this->b = new UNDEFINED();
}
function init( $a = '' , $b = '') {
$this->a = $this->setValue($a,$this->a);
$this->b = $this->setValue($b,$this->b);
}
function setValue($a,$default) {
return $a === '' ? $default : $a;
}
function isUndefined($a) {
return $a instanceof UNDEFINED;
}
public function isFullyLoaded()
{
$result = true;
$properties = get_object_vars($this);
print_r($properties);
foreach ($properties as $property){
$result = $result && !$this->isUndefined($property);
if ( !$result) break;
}
return $result;
}
function printStatus() {
if($this->isFullyLoaded() ) {
echo 'Loaded!';
} else {
echo 'Not loaded';
}
}
}
$test= new Test(1);
$test->printStatus();
$test->init('hello');
$test->printStatus();
$test->init('', null);
$test->printStatus();
Use property_exists():
<?php
error_reporting(E_ALL);
// oop:
class A {
public $null_var = null;
}
$a = new A;
if(property_exists($a, 'null_var')) {
echo "null_var property exists\n";
}
if(property_exists($a, 'unset_var')) {
echo "unset_var property exists\n";
}
// procedural:
$null_var = null;
if(array_key_exists('null_var', $GLOBALS)) {
echo "null_var variable exists\n";
}
if(array_key_exists('unset_var', $GLOBALS)) {
echo "unset_var variable exists\n";
}
// output:
// null_var property exists
// null_var variable exists
My class is like this:
<?php
class ExampleClass{
private $example_property = false;
public function __construct(){
$this->example_property = function() {
$this->example_property = 1;
return $this->example_property;
};
}
public function get_example_property(){
return $this->example_property;
}
}
$example = new ExampleClass();
echo $example->get_example_property();
Property $example_property must be false until you call it, then, the first time it is called, I want to assign 1 to it. What's wrong with my code?
Error: Error Object of class Closure could not be converted to string on line number 20.
I just tried to play a little bit with your code.
To make it possible, you'll have to find out, whether your variable is a function (defined in your __construct) or the number set by this function
<?php
class ExampleClass{
private $example_property = false;
public function __construct(){
$this->example_property = function() {
$this->example_property = 1;
return $this->example_property;
};
}
public function get_example_property(){
$func = $this->example_property;
if(is_object($func) && get_class($func)=='Closure'){
return $func();
}
return $func;
}
}
$example = new ExampleClass();
echo $example->get_example_property(); //returning 1
echo $example->get_example_property(); //returning 1 again
But anyway, I don't see any sense in doing this.
The typical solution would be something like this:
class ExampleClass{
private $example_property = false;
public function __construct(){
//usually initializing properties goes here.
//$this->example_property = 1;
}
public function get_example_property(){
// I think you want a value to be initialzed only if needed.
// so then it can go here.
if(!$this->example_property) {
$this->example_property = 1; //initialize it, if needed
}
return $this->example_property;
}
}
$example = new ExampleClass();
echo $example->get_example_property(); //returning 1
echo $example->get_example_property(); //returning 1 again
I have this code:
<?php
$db_initiallized = false;
$db_connection = NULL;
function db_init()
{
global $db_initiallized, $db_connection;
if(!$db_initiallized) //error occurs on this line
{
$db_connection = mysql_connect("server", "username", "password");
if(!$db_connection)
{
echo 'Connection failure!';
exit();
}
$db_initiallized = true;
}
}
?>
And I get the error:
Use of undefined constant
I'm not sure why this error is occurring. Perhaps I am declaring global variables wrong. What's going on here?
The $GLOBALS array can be used instead:
$GLOBALS['db_initiallized'] = false;
$GLOBALS['db_connection'] = NULL;
function db_init(){
echo $GLOBALS['db_initiallized'];
echo $GLOBALS['db_connection'];
}
OR
If the variable is not going to change you could use define.
define('db_initiallized', FALSE);
define('db_connection', NULL);
function db_init()
{
echo db_initiallized;
echo db_connection;
}
OR
If you have a set of functions that need some common variables, a class with properties may be a good choice instead of a global:
class MyTest
{
protected $a;
public function __construct($a)
{
$this->a = $a;
}
public function head()
{
echo $this->a;
}
public function footer()
{
echo $this->a;
}
}
$a = 'localhost';
$obj = new MyTest($a);
How can i pass a class as a parameter in my function
So far i've tried
$sc = new SampleClass();
SampleFunction($sc);
function SampleFunction(&$refClass)
{
echo $refClass->getValue();
}
this is a simplified example of what im doing.. i actually have to do complex procedures inside this sample function. I'm not getting any response from the sample function. What am i doing wrong? thank you
UPDATE
char.php
class Charss {
var $name=0;
var $hp=500;
var $spd=10;
var $rtime=10;
var $dmg=10;
function __construct( $name, $hp, $spd, $rtime , $dmg) {
$this->name = $name;
$this->hp = $hp;
$this->spd = $spd;
$this->rtime = $rtime;
$this->dmg = $dmg;
}
function get_name() {
return $this->name;
}
function set_name($new_name) {
$this->name = $new_name;
}
function get_hp() {
return $this->hp;
}
function set_hp($new_hp) {
$this->hp = $new_hp;
}
function get_spd() {
return $this->spd;
}
function set_spd($new_spd) {
$this->spd = $new_spd;
}
function get_rtime() {
return $this->rtime;
}
function set_rtime($new_rtime) {
$this->rtime = $new_rtime;
}
function get_dmg() {
return $this->get_dmg;
}
function set_dmg($new_dmg) {
$this->dmg = $new_dmg;
}
}
myclass.php
require("char.php");
class Person {
function try_process()
{
$chr1 = new Charss("Player1",500,3,0,50);
$chr2 = new Charss("Player2",500,6,0,70);
while ($chr1->get_hp() > 0 && $chr2->get_hp() > 0)
{
$sth = min($chr1->get_rtime(), $chr2->get_rtime());
if ($chr1->get_rtime() == 0 && $chr2->get_rtime() > 0)
{
exit;
Fight($chr1,$chr2);
$chr1->set_rtime($chr1->get_spd());
}
elseif ($chr2->get_rtime() == 0 && $chr1->get_rtime() > 0)
{
Fight($chr2,$chr1);
$chr2->set_rtime($chr2->get_spd());
}
else
{
Fight($chr1,$chr2); #having trouble with this
$chr1->set_rtime($chr1->get_spd());
}
$chr1->set_rtime($chr1->get_rtime() - $sth);
$chr2->set_rtime($chr2->get_rtime() - $sth);
}
}
function Fight($atk,$def)
{
$def->set_hp($def->get_hp() - $atk->get_dmg());
echo $atk->get_name() . " attacked " . $def->get_name() . " for " . $atk->get_dmg() . " damage";
}
}
so im calling the function try_process on button click
What you're actually doing there is passing an object, not a class.
$sc = new SampleClass();
creates an instance of SampleClass, aka an object.
I assume there's some error being thrown elsewhere as what you have is correct.
I tested the following code and got the expected output:
class SampleClass
{
public function getValue()
{
return 4;
}
}
$sc = new SampleClass();
SampleFunction($sc);
function SampleFunction(&$refClass)
{
echo $refClass->getValue();
}
Output: 4
If you provide more details of your actual code we might be able to determine the problem.
I can't see anything wrong with your code
using &$refClass is however is not recommended and I guess willbe removed from future iteration of PHP version
but here is an example
class objects are passed as reference I suppose so no need of '&'
http://ideone.com/GbmUy
Why is the function argument a reference? Probably shouldn't be.
Other than that, there's nothing wrong with you posted, so the error is likely within SampleClass.
Others have answered pretty well, but this is a silly little example to show you how to modify the class (either by calling a property setter, or setting public properties directly)
class foo {
private $member1;
public $member2;
public function __construct($member1,$member2) {
$this->member1=$member1;
$this->member2=$member2;
}
public function SetMember1($value) {
$this->member1 = $value;
}
public function GetMember1() {
return $this->member1;
}
}
function SetMembers(foo $obj, $member1, $member2) {
// Call a setter
$obj->SetMember1($member1);
// Set a member variable directly
$obj->member2 = $member2;
}
$obj = new foo('default member 1', 'default member 2');
echo "member1 (before): {$obj->GetMember1()}\n";
echo "member2 (before): {$obj->member2}\n";
// Change values
SetMembers($obj, 'new member1', 'new member2');
echo "member1 (after): {$obj->GetMember1()}\n";
echo "member2 (after): {$obj->member2}\n";
This will output:
member1 (before): default member 1
member2 (before): default member 2
member1 (after): new member1
member2 (after): new member2
Can anyone please explain to me why the following code does not set the values on the array as expected? $_SESSION['foo'] stays empty, even after assigning time() and rand(). I've checked, the __get accessor method is actually called when assigning the variables but they aren't stored for one reason or another.
$test = Session::getSession('test');
$test->foo = array();
$test->foo[] = time();
$test->foo['baz'] = rand(1,9);
var_dump($_SESSION);
Using this simple Session wrapper
class Session
{
protected $namespace = null;
public static function getSession($namespace)
{
return new Session($namespace);
}
public static function destroySession($namespace)
{
if(isset($_SESSION[$namespace])) {
unset($_SESSION[$namespace]);
return true;
}
return false;
}
private function __construct($namespace)
{
$this->namespace = $namespace;
if(!isset($_SESSION[$namespace])) {
$_SESSION[$namespace] = null;
}
}
public function &__get($name)
{
return (isset($_SESSION[$this->namespace][$name])) ? $_SESSION[$this->namespace][$name] : null;
}
public function __set($name, $value)
{
$_SESSION[$this->namespace][$name] = $value;
}
}
In case it might be relevant, i'm using php 5.3.6
I 'm not sure if this can be made to work at all.
For one, to return by reference you should add the & operator at the call site as well. I 'm not sure how that might be possible without screwing up the nice syntax you 're trying to achieve.
Also, you cannot return expressions by reference (only variables). So this won't work:
public function &__get($name)
{
return (isset($_SESSION[$this->namespace][$name]))
? $_SESSION[$this->namespace][$name]
: null;
}
At the very least it should be written as
public function &__get($name)
{
$value = isset($_SESSION[$this->namespace][$name])
? $_SESSION[$this->namespace][$name]
: null;
return $value;
}