I've come across some unexpected behavior with static variables defined inside object methods being shared across instances. This is probably known behavior, but as I browse the PHP documentation I can't find instances of statically-defined variables within object methods.
Here is a reduction of the behavior I've come across:
<?php
class Foo {
public function dofoo() {
static $i = 0;
echo $i++ . '<br>';
}
}
$f = new Foo;
$g = new Foo;
$f->dofoo(); // expected 0, got 0
$f->dofoo(); // expected 1, got 1
$f->dofoo(); // expected 2, got 2
$g->dofoo(); // expected 0, got 3
$g->dofoo(); // expected 1, got 4
$g->dofoo(); // expected 2, got 5
Now, I would have expected $i to be static per instance, but in reality $i is shared between the instances. For my own edification, could someone elaborate on why this is the case, and where it's documented on php.net?
This is the very definition of static.
If you want members to be specific to an instance of an object, then you use class properties
e.g.
<?php
class Foo
{
protected $_count = 0;
public function doFoo()
{
echo $this->_count++, '<br>';
}
}
Edit: Ok, I linked the documentation to the OOP static properties. The concept is the same though. If you read the variable scope docs you'll see:
Note: Static declarations are resolved in compile-time.
Thus when your script is compiled (before it executes) the static is "setup" (not sure what term to use). No matter how many objects you instantiate, when that function is "built" the static variable references the same copy as everyone else.
I agree that the current PHP documentation is not sufficiently clear on exactly what "scope" means for a static variable inside a non-static method.
It is of course true (as hobodave indicates) that "static" generally means "per class", but static class properties are not exactly the same thing as static variables within a (non static) method, in that the latter are "scoped" by method (every method in a class can have its own static $foo variable, but there can be at most one static class member named $foo).
And I would argue that although the PHP 5 behavior is consistent ("static" always means "one shared instance per class"), it is not the only way that PHP could behave.
For example, most people use static function variables to persist state across function calls, and for global functions the PHP behavior is exactly what most everyone would expect. So it is certainly possible to imagine a PHP interpreter that maintains the state of certain method variables across method invocation and does so "per instance", and that's actually what I also expected to happen the first time I declared a local method variable to be static.
That is what static is, it's the same variable across all instances of the class.
You want to write this so that the variable is a private member of the instance of the class.
class Foo {
private $i = 0;
public function dofoo() {
echo $this->i++ . '<br>';
}
}
The static keyword can be used with variables, or used with class methods and properties. Static variables were introduced in PHP 4 (I think, it might have been earlier). Static class members/methods were introduced in PHP 5.
So, per the manual, a static variable
Another important feature of variable scoping is the static
variable. A static variable exists only in a local function
scope, but it does not lose its value when program execution
leaves this scope.
This is consistant with the behavior you described. If you want a per instance variable, used a regular class member.
Ups 7 years it a long time but anyway here it goes.
All classes have a default constructor why am I saying this?!?
Because if you define a default behaviour in constructor each instance of the class will be affected.
Example:
namespace Statics;
class Foo
{
protected static $_count;
public function Bar()
{
return self::$_count++;
}
public function __construct()
{
self::$_count = 0;
}
}
Resulting in:
require 'Foo.php';
use Statics\Foo;
$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
0
1
2
0
1
2
Every new instance from the upper class will start from 0!
The static count behaviour will NOT be shared across the multiple instances as it will be starting from the value assigned in constructor.
If you need to share data across multiple instances all you need to do is to define a static variable and assign default data outside the constructor!
Example:
namespace Statics;
class Foo
{
//default value
protected static $_count = 0;
public function Bar()
{
return self::$_count++;
}
public function __construct()
{
//do something else
}
}
Resulting in:
require 'Foo.php';
use Statics\Foo;
$bar = new Foo();
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
echo $bar->bar().'<br>';
$barcode = new Foo();
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
echo $barcode->bar().'<br>';
0
1
2
3
4
5
As you can see the results are completely different, the memory space allocation is the same in between class instances but it can produce different results based on how you define default value.
I hope it helped, not that the above answers are wrong but I felt that it was important to understand the all concept from this angle.
Regards, from Portugal!
Related
First, take a look at this PHP 5.5.8 code which implements lazy initialization of class properties with using a Trait:
trait Lazy
{
private $__lazilyLoaded = [];
protected function lazy($property, $initializer)
{
echo "Initializer in lazy() parameters has HASH = "
. spl_object_hash($initializer) . "\n";
if (!property_exists($this, $property)
|| !array_key_exists($property, $this->__lazilyLoaded))
{
echo "Initialization of property " . $property . "\n";
$this->__lazilyLoaded[$property] = true;
$this->$property = $initializer();
}
return $this->$property;
}
}
class Test
{
use Lazy;
private $x = 'uninitialized';
public function x()
{
return $this->lazy('x', function(){
return 'abc';
});
}
}
echo "<pre>";
$t = new Test;
echo $t->x() . "\n";
echo $t->x() . "\n";
echo "</pre>";
The output is as follow:
uninitialized
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
Initialization of property x
abc
Initializer in lazy() parameters has HASH = 000000001945aafc000000006251ed62
abc
Here are my questions and things I'd like to discuss and improve, but I don't know how.
Based on the HASH values reported, it may appear that the initializer function is created only once.
But actually uniqueness is not guaranteed between objects that did not reside in memory simultaneously. So the question remains unanswered - whether the initializer gets created only once, and it matters for performance I think, but I'm not sure.
The way it's implemented now is not very safe in that if I refactor the code and change property $x to something else, I might forget to change the 'x' value as a first parameter to lazy() method. I'd be happy to use & $this->x instead as a first parameter, but then inside lazy() function I don't have a key to use for $__lazilyLoaded array to keep track of what has been initialized and what has not. How could I solve this problem? Using hash as a key isn't safe, nor it can be generated for callbacks like array($object, 'methodName')
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to access the raw $this->x property as it can be still uninitialized. So I wonder is there a better way - maybe I should save all the values in some Trait's field?
The global aim is to make it:
a) Fast - acceptable enough for small and medium software applications
b) Concise in syntax - as much as possible, to be used widely in the methods of the classes which utilize the Lazy trait.
c) Modular - it would be nice if objects still held their own properties; I don't like the idea of one super-global storage of lazily-initialized values.
Thank you for your help, ideas and hints!
So the question remains unanswered - whether the
initializer gets created only once, and it matters for performance I
think, but I'm not sure.
Well, closure instance is created only once. But anyway, performance will depend not on closure instance creation time (since it is insignificant), but closure execution time.
I'd be happy to use & $this->x instead as a first parameter, but then
inside lazy() function I don't have a key to use for $__lazilyLoaded
array to keep track of what has been initialized and what has not. How
could I solve this problem? Using hash as a key isn't safe, nor it can
be generated for callbacks like array($object, 'methodName')
I can propose the following solution:
<?php
trait Lazy
{
private $_lazyProperties = [];
private function getPropertyValue($propertyName) {
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
if(!isset($this->_propertyLoaders[$propertyName])) {
throw new Exception("Property $propertyName does not have loader!");
}
$propertyValue = $this->_propertyLoaders[$propertyName]();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
public function __call($methodName, $arguments) {
if(strpos($methodName, 'get') !== 0) {
throw new Exception("Method $methodName is not implemented!");
}
$propertyName = substr($methodName, 3);
if(isset($this->_lazyProperties[$propertyName])) {
return $this->_lazyProperties[$propertyName];
}
$propertyInializerName = 'lazy' . $propertyName;
$propertyValue = $this->$propertyInializerName();
$this->_lazyProperties[$propertyName] = $propertyValue;
return $propertyValue;
}
}
/**
* #method getX()
**/
class Test
{
use Lazy;
protected function lazyX() {
echo("Initalizer called.\r\n");
return "X THE METHOD";
}
}
echo "<pre>";
$t = new Test;
echo $t->getX() . "\n";
echo $t->getX() . "\n";
echo "</pre>";
Result:
c:\Temp>php test.php
<pre>X THE METHOD
X THE METHOD
</pre>
c:\Temp>php test.php
<pre>Initalizer called.
X THE METHOD
X THE METHOD
</pre>
c:\Temp>
You cannot always be protected from forgetting something, but it is easier to remember when all things are close to each other. So, I propose to implement lazy loaders as methods on corresponding classes with specific names. To provide autocomplete #method annotation can be used. In a good IDE refactoring method name in annotation will allow to rename method across all project. Lazy loading function will be declared in the same class so renaming it also is not a problem.
By declaring a function with a name, starting with "lazy", in my example you both declare a corresponding accessor function, with name starting with "get" and it's lazy loader.
If $this->x is a private property, it's safe for outer world to call the x() method, but for the class' methods it's still unsafe to
access the raw $this->x property as it can be still uninitialized. So
I wonder is there a better way - maybe I should save all the values in
some Trait's field?
Trait fields are available in the class, that uses specific trait. Even private fields. Remember, this is composition, not inheritance. I think it's better to create private trait array field and store your lazy properties there. No need to create a new field for every property.
But I cannot say I like the whole scheme. Can you explain the use of it for you? May be we can come with better solution.
How to combine two variables to obtain / create new variable?
public $show_diary = 'my';
private my_diary(){
return 1;
}
public view_diary(){
return ${"this->"}.$this->show_diary.{"_diary()"}; // 1
return $this->.{"$this->show_diary"}._diary() // 2
}
both return nothing.
Your class should be like following:
class Test
{
public $show_diary;
function __construct()
{
$this->show_diary = "my";
}
private function my_diary(){
return 707;
}
public function view_diary(){
echo $this->{$this->show_diary."_diary"}(); // 707
}
}
It almost looks from your question like you are asking about how to turn simple variables into objects and then how to have one object contain another one. I could be way off, but I hope not:
So, first off, what is the differnce between an object and a simple variable? An object is really a collection of (generally) at least one property, which is sort of like a variable within it, and very often functions which do things to the properties of the object. Basically an object is like a complex variable.
In PHP, we need to first declare the strucutre of the object, this is done via a class statement, where we basicaly put the skeleton of what the object will be into place. This is done by the class statement. However, at this point, it hasn't actually been created, it is just like a plan for it when it is created later.
The creation is done via a command like:
$someVariable= new diary();
This executes so create a new variable, and lays it out with the structure, properties and functions defined in the class statement.
From then on, you can access various properties or call functions within it.
class show_diary
{
public $owner;
public function __construct()
{
$this->owner='My';
}
}
class view_diary
{
public $owner;
public $foo;
public function __construct()
{
$this->foo='bar';
$this->owner=new show_diary();
}
}
$diary= new view_diary();
print_r($diary);
The code gives us two classes. One of the classes has an instance of the other class within it.
I have used constructors, which are a special type of function that is executed each time we create a new instance of a class - basically each time we declare a variable of that type, the __construct function is called.
When the $diary= new view_diary(); code is called, it creates an instance of the view_diary class, and in doing so, the first thing it does is assigns it's own foo property to have the value 'bar' in it. Then, it sets it's owner property to be an instance of show_diary which in turn then kicks off the __construct function within the new instance. That in turn assigns the owner property of the child item to have the value 'My'.
If you want to access single properties of the object, you can do so by the following syntax:
echo $diary->foo;
To access a property of an object inside the object, you simply add more arrows:
echo $diary->owner->owner;
Like this?
$diary = $this->show_diary . '_diary';
return $this->$diary();
I'm learned php as functional and procedure language. Right now try to start learn objective-oriented and got an important question.
I have code:
class car {
function set_car($model) {
$this->model = $model;
}
function check_model()
{
if($this->model == "Mercedes") echo "Good car";
}
}
$mycar = new car;
$mycar->set_car("Mercedes");
echo $mycar->check_model();
Why it does work without declaration of $model?
var $model; in the begin?
Because in php works "auto-declaration" for any variables?
I'm stuck
Every object in PHP can get members w/o declaring them:
$mycar = new car;
$mycar->model = "Mercedes";
echo $mycar->check_model(); # Good car
That's PHP's default behaviour. Those are public. See manual.
Yes, if it doesn't exist, PHP declares it on the fly for you.
It is more elegant to define it anyway, and when working with extends it's recommended, because you can get weird situations if your extends are gonna use the same varnames and also don't define it private, protected or public.
More info:
http://www.php.net/manual/en/language.oop5.visibility.php
PHP class members can be created at any time. In this way it will be treated as public variable. To declare a private variable you need to declare it.
Yes. But this way variables will be public. And declaration class variable as "var" is deprecated - use public, protected or private.
No, it's because $model is an argument of the function set_car. Arguments are not exactly variables, but placeholders (references) to the variables or values that will be set when calling the function (or class method). E.g., $model takes the value "Mercedes" when calling set_car.
I think this behavior can lead to errors.
Lets consider this code with one misprint
declare(strict_types=1);
class A
{
public float $sum;
public function calcSum(float $a, float $b): float
{
$this->sum = $a;
$this->sums = $a + $b; //misprinted sums instead of sum
return $this->sum;
}
}
echo (new A())->calcSum(1, 1); //prints 1
Even I use PHP 7.4+ type hints and so one, neither compiler, nor IDE with code checkers can't find this typo.
I have a string containing the class name and I wish to get a constant and call a (static) method from that class.
<?php
$myclass = 'b'; // My class I wish to use
$x = new x($myclass); // Create an instance of x
$response = $x->runMethod(); // Call "runMethod" which calls my desired method
// This is my class I use to access the other classes
class x {
private $myclass = NULL;
public function __construct ( $myclass ) {
if(is_string($myclass)) {
// Assuming the input has a valid class name
$this->myclass = $myclass;
}
}
public function runMethod() {
// Get the selected constant here
print $this->myclass::CONSTANT;
// Call the selected method here
return $this->myclass::method('input string');
}
}
// These are my class(es) I want to access
abstract class a {
const CONSTANT = 'this is my constant';
public static function method ( $str ) {
return $str;
}
}
class b extends a {
const CONSTANT = 'this is my new constant';
public static function method ( $str ) {
return 'this is my method, and this is my string: '. $str;
}
}
?>
As I expected (more or less), using $variable::CONSTANT or $variable::method(); doesn't work.
Before asking what I have tried; I've tried so many things I basically forgot.
What's the best approach to do this? Thanks in advance.
To access the constant, use constant():
constant( $this->myClass.'::CONSTANT' );
Be advised: If you are working with namespaces, you need to specifically add your namespace to the string even if you call constant() from the same namespace!
For the call, you'll have to use call_user_func():
call_user_func( array( $this->myclass, 'method' ) );
However: this is all not very efficient, so you might want to take another look at your object hierarchy design. There might be a better way to achieve the desired result, using inheritance etc.
in php 7 you can use this code
echo 'my class name'::$b;
or
#Uncomment this lines if you're the input($className and $constName) is safe.
$reg = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/';
if(preg_match($reg,$className) !== 1 || preg_match($reg,$constName) !== 1)
throw new \Exception('Oh, is it an attack?');
$value = eval("return $className::$constName;");
You can achieve it by setting a temporary variable. Not the most elegant way but it works.
public function runMethod() {
// Temporary variable
$myclass = $this->myclass;
// Get the selected constant here
print $myclass::CONSTANT;
// Call the selected method here
return $myclass::method('input string');
}
I guess it's to do with the ambiguity of the ::, at least that what the error message is hinting at (PHP Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM)
Use call_user_func to call static method:
call_user_func(array($className, $methodName), $parameter);
Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method's signature - they cannot define the implementation.
When inheriting from an abstract class, all methods marked abstract in the parent's class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private. Furthermore the signatures of the methods must match, i.e. the type hints and the number of required arguments must be the same. This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures could differ.
Refer to http://php.net/manual/en/language.oop5.abstract.php
This might just be tangential to the subject but, while searching for my own issue I found that the accepted answer pointed me in the right direction, so I wanted to share my problem & solution in case someone else might be stuck in a similar fashion.
I was using the PDO class and was building some error options from an ini config file. I needed them in an associative array in the form: PDO::OPTION_KEY => PDO::OPTION_VALUE, but it was of course failing because I was trying to build the array with just PDO::$key => PDO::$value.
The solution (inspired from the accepted answer):
$config['options'] += [constant('PDO::'.$key) => constant('PDO::'.$option)];
where everything works if you concatenate the class name and the Scope Resolution Operator as a string with the variable and get the constant value of the resulting string through the constant function (more here).
Thank you and I hope this helps someone else!
Regarding PHP, what are the differences between:
get_class_vars()
get_object_vars()
get_class_vars() takes the class_name
get_object_vars() takes an $object variable
They both function similarly:
get_class_vars() will expose default public variables (or private/protected if called within the class)
get_object_vars() will expose the current public variables (or private/protected if called within the class object)
Neither will expose methods.
As you can see from the get_class_vars and get_object_vars manual pages, get_class_vars gets the default values of properties of a class, and get_object_vars gets the current values of properties of an object.
Furthermore, get_class_vars takes a string (ie. the name of a class), whereas get_object_vars takes an object.
class Example
{
public $var = 123;
}
$e = new Example();
$e->var = 456;
var_dump(get_class_vars("Example"));
/*
array(1) {
["var"]=>
int(123)
}
*/
var_dump(get_object_vars($e));
/*
array(1) {
["var"]=>
int(456)
}
*/
Theres one more thing:
get_object_vars() doesn't see static variables, but sees vars created during runtime!
get_class_vars() DOES SEE the static ones, but DOES NOT see new variables created during runtime, eg. in the constructor:
public function __construct() {$this->newval = "newval";}
As of PHP 5.5.9
I have noticed that there's actually a slight difference between get_class_vars and get_object_vars.
Regarding to PHP Bug Report, and I have tested it myself as well.
Both methods return the same result. BUT, you can manipulate object properties by using get_object_vars and not with get_class_vars.
Consider the following sample,
class Foo
{
public $bar = null;
public function __construct() {
$this->bar = new DateTime(); // Now
$this->far = &$this->bar;
}
}
$foo = new Foo();
var_dump($foo);
$vars = get_object_vars($foo);
$vars['bar'] = new DateTime('2014-03-25');
var_dump($foo);
In the sample above, you will notice that the second var_dump will
have $foo->bar modified.
Note: If you try to change the method from get_object_vars to
get_class_vars, you will also notice that $foo->bar will not be
modified.
get_class_vars() gives you even nonstatic variables, but their values are taken at the time of creating object, eg. init values.
I ran this very unscientific (and probably quite error prone) performance test:
I used this class:
class foo {
public $bar;
public $baz = "abcd";
public $qux = 123;
public $quux;
public $quuz = "abcd";
public $corge = 1234;
}
and did test 1 (get_object_vars):
$start = microtime(true);
for($i = 1; $i >= 100000; $i++) {
$foo = new foo;
$gov = get_object_vars($foo);
}
$end = microtime(ture);
echo $end - $start;
then test 2 (get_class_vars - replacing the inside of the for loop):
$gcv = get_class_vars("foo");
then test 3 (get_class_vars, and assuming you need to instantiate the object anyway - again replacing the for loop):
$foo = new foo();
$gcv = get_class_vars("foo");
I ran each one 5 times and these are the results I got:
test 1:
0.00014019012451172
0.00014519691467285
0.00014495849609375
0.00012087821960449
0.00012421607971191
AVG: 0.0001350879669
test 2:
0.00013995170593262
0.00012898445129395
0.00010204315185547
0.00013494491577148
0.00061392784118652
AVG: 0.0002239704132
(if we remove the outlier): 0.0001264810562
test 3:
0.00014710426330566
0.00012397766113281
0.00012612342834473
0.00011992454528809
0.00014090538024902
AVG: 0.0001316070557
In test two there was a weird outlier in round 5... not sure why... hiccup on my server. If we remove that outlier, it looks like get_class_vars() is slightly more performant, regardless of whether you need to instantiate the class.
This test does not take into account any variable caching or php memory optimization that may be going on. Of course in a real world application it's highly unlikely that you'll iterate over either of the functions 10k times. So the functions' single and first execution performance is really more interesting, but much harder to test (accurately).