Why does PHP require you to explicitly write $this? I would understand if you had to use $this here:
function foo($bar) {
$this->bar = $bar;
}
But you must write it explicitly in verbose code that looks like this:
$this->var3 = globalFun($this->var, $this->var2[$this->anotherVar], $this->method());
as opposed to:
$var3 = globaFun($var, $var2[$anotherVar], method());
So what is the point of $this?
Additional Bonus Question:
Why do we have to differentiate static references and instances? Why do we need:
static function getValue() {
return self::value;
}
Can't PHP find out at runtime if the variable/method in question is static? Now if I want to change a method from static to non-static, I have to replace all those self:: with $this-> (and vice-versa).
Wouldn't it be better if we had a $this that behaves like it does in Java?
Since this was re-opened, I'll post here my answer, as promised.
TL;DR version If it were not required to qualify a member access, there would be not only performance penalties, but the same line of code could simultaneously signify a field access and a local variable access, depending on the code path.
Full version
In PHP, there's always one symbol table active at the table. This is either the global symbol table or a function/method local symbol table (which by the way, are lazily built). Superglobals and optimizations like compiled variables aside, when a variable $var is requested, it is looked up in the current symbol table. Since the object properties live not on the symbol tables, but instead on either in the objects (the instance properties) or the structure associated the class (the static properties), a lookup for $var can never return a property.
To bring a given variable to the function scope, you must explicitly signal your intention by creating a reference. Examples:
$myglobal = 7;
class A {
private $prop;
public function meth() {
global $myglobal; //bring a global to the current scope
$prop =& $this->prop; //brings a property to the current scope
$local = 4;
$lambda = function () use (&$local) { };
}
}
Obviously, this is just a more sophisticated way to phrase what currently happens. The question is why this behavior?
After all, in Java we only have to type this.prop when there's a local variable called prop hiding the property. Why is this not a good option for PHP?
I can think of several reasons.
The object properties are determined at runtime
PHP has something called "dynamic properties". You can assign new properties to objects at runtime. In fact given two objects of the same class, one can have a given property $a and while the other doesn't. Example:
$obj1 = new stdClass();
$obj2 = new stdClass();
$obj1->a = 7;
In PHP, the defined local variables are determined at runtime
Variables do not have to be declared; consequently, depending on the code path, at some point a variable may or may not be defined. To add insult to the injury, we also have the monster called "variable variables". Example:
class A {
private $g = 3;
public function func($varname) {
if (rand(1,2) == 1) {
$g = 4; //no block scope; the scope is the function's
}
$$varname = 5; //god knows what's happening here
//if local variables hid properties, we'd have trouble
}
}
In Java, a given identifier may also represent, inside the same function, a local variable and a property, but:
Not within the same block (in PHP, all blocks in a function share exactly the same scope).
You get a warning if you're hiding a property.
Crucially, in any given occurrence of an identifier, it's either a property or a local variable, it can't sometimes be one and other times the other.
Consequences
Owing to these facts, it would be impossible to determine at compile time if $var referred to a local variable or to a property. Consequently:
At runtime, every time a variable occurred, it would have to looked up first in the local symbol table, then in the instance properties table, and finally in the static properties list, or any other order (since there can't be an instance and a static property with the same name and static properties need to be declared, there would be some optimization potential here, but the point stands). This means a symbol would have, in the worst case, would have to be looked up in three different places. This is bad from a performance perspective.
A given symbol occurrence could mean different things on different occasions. This is a recipe for disaster.
Okay, so let's remove the need for writing $this everywhere. Take a look at this situation:
class Foo {
public function setBar($value) {
$bar = $value;
}
}
$foo = new Foo();
$foo->setBar('some value');
Is $bar a local variable or a member of $foo?
There has to be some differentiation. They could have allowed declaration of local variables with the var keyword, but that would not have been backwards-compatible and would have been very confusing to people upgrading from older versions of PHP.
Same thing applies to self::. How does the interpreter know whether the function you wanted to call is global or specific to the class?
PHP was not OOP.
Now it is, but with side effects.
Actually, I know people who use this. in Java even where unnecessary because they feel it creates clearer code ;) I don't have a really definite answer, but I guess that, internally, getting $var would always have to be translated to $this->var. So it's not like someone intentionally made things more complicated by forcing us to do $this->var, but just decided to not implement the $var shortcut. If that helps in any way, I don't know ;)
Related
Alright so I think this may be extremely basic, but it has me stumped nonetheless. Before I get to my question, let me demonstrate the concept my question is based on with this working example:
<?php
$a = 'Stack';
$b = $a.' Overflow';
echo $b; // Result: "Stack Overflow"
?>
In the above example, $b is defined as the combination of $a and ' Overflow'.
Now, let's assume I want to do the same thing as above except I don't want to use global variables. I want to use classes. This is how I have attempted to achieve that:
<?php
class ClassName {
public $a = 'Stack';
public $b = $this->a.' Overflow'; // This gives me: "Parse error: syntax error, unexpected '$this'"
}
$instantiate = new ClassName;
echo $instantiate->$b; // Desired result: "Stack Overflow"
?>
As stated, this attempt results in an error. To me, this attempt seems logical, but I guess PHP doesn't think so.
Question: Is this possible, and if so, how do I go about achieving the desired result? Also, if you could explain why my attempt has failed (logically), that would be a bonus.
I've searched and researched for hours on end trying to find an answer or figure this out on my own, but for the life of me, I cannot find anyone or anything that even touches on this (including other Stack Overflow threads). I can't even find anywhere saying it's impossible or anything of the sort either.
I'm a PHP novice, so I may need more explanation than others, but any kind of help or general guidance would be much appreciated. Thank you.
You cannot use $this when defining the class because it refers to a concrete object context which becomes available after instantiation. You can use a constructor for that kinds of stuff.
class ClassName
{
public $a = 'Stack';
public $b = "";
function __construct()
{
$this->b = $this->a.' Overflow';
}
}
$instantiate = new ClassName;
echo $instantiate->b;
Quoting from the PHP Docs about defining class properties
They are defined by using one of the keywords public, protected, or private, followed by a normal variable declaration. This declaration may include an initialization, but this initialization must be a constant value -- that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.
(my emphasis)
Concatenation cannot be evaluated at compile time, only at run time.
If you need to do this, then define the initial value for the property in the constructor
This would throw a syntax error because you are using $this in a scope that it is not allowed to:
The pseudo-variable $this is available inside any class method when that method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object).
This means, even if you wanted you won't be able to do what you want to do because of such restriction. This is a common restriction that is in many programming languages, properties have to be initialized to static values. To solve your issue you can do any of the following:
In your constructor, create those variables.
Create the second variable on its own, and create a method that concatenates the two.
Create a magic method to do it for you.
The same restriction holds true for static class properties.
If I have a public class method that is returning a reference to a non-visible (private or protected) property, I can use that reference to gain direct access:
PHP code
class A
{
private $property = 'orange';
public function &ExposeProperty()
{
return $this->property;
}
public function Output()
{
echo $this->property;
}
}
$obj = new A();
# prints 'orange'
$obj->Output();
$var = &$obj->ExposeProperty();
$var = 'apple';
# prints 'apple'
$obj->Output();
Is there a reasoning behind this functionality in PHP? Or is it just a design oversight, failing to keep track of access violations through references?
It obviously comes in handy when you want to achieve something like:
PHP code
$this->load->resource();
Where load is an object that modifies given properties of $this. But apart from this shortcut, I don't see many possible uses which wouldn't be possible with valid OOP patterns otherwise.
Well, you are explicitly returning a reference to a value. You're locking the front door, but are then opening a side entrance. You are very deliberately taking aim and shooting your own foot here. If $property was an object, and you'd return this object with or without & reference, any modifications to this object would be reflected on $property as well. That's how a reference works, it always modifies the one and only existing value that the reference points to.
Visibility modifiers aren't magic iron clad "protections". There are any number of ways how you can circumvent a private visibility to access and modify the property. They're mostly there as a flag to yourself and other developers that this property should not be accessed directly, it's for internal use and not a publicly sanctioned API. And PHP will slap you on the wrist should you forget that. Nothing more, nothing less.
Also, nothing is really being violated here. Outside code is at no point accessing or modifying $obj->property. That's the only thing private is supposed to prohibit. You're essentially exposing a public API on your object which modifies a private property. Usually this is done with getter and setter functions, but a by-reference API obviously works as well.
This may be a basic question, but it has kept me wondering for quite some time now.
Should I declare all private/local variables being private? Or is this only necessary for "important" variables?
For instance, I have the (temporary) result of a calculation. Should I pre-declare this variable?
Hope someone can point this out.
Since you're talking about private, protected and public I take it you're talking about properties, instead of variables.
In that case: yes, you should declare them beforehand.
Because of how PHP objects are designed, an array (properties_table) is created on compile time. This array ensures that accessing a given property is as fast as possible. However, if you add properties as you go along, PHP needs to keep track of this, too. For that reason, an object has a simple properties table, too.
Whereas the first (properties_table) is an array of pointers, the latter is a simple key => value table.
So what? Well, because the properties_table contains only pointers (which are of a fixed size), they're stored in a simple array, and the pointers are fetched using their respective offsets. The offsets are stored in yet another HashTable, which is the ce->properties_info pointer.
As bwoebi pointed out to me in the comments: getting the offset (HashTable lookup) is a worst-case linear operation (O(n)) and predefined property lookups are constant-time complex operations (O(1)). Dynamic properties, on the other hand need another HashTable lookup, a worst-case linear operation (O(n)). Which means that, accessing a dynamic property takes in average about twice as long. Authors of the Wikipedia can explain Time-Complexity far better than I can, though.
At first, access modifiers might seem irrelevant. As you go along, you'll soon find that sometimes, you just don't want to take the chance that some property of some object gets modified by some bit of code. That's when you see the value of private.
If an object contains another object, that holds all sorts of settings that your code will rely upon, for example, you'll probably use a getter method to access those settings from the outside, but you'll leave that actual property tucked away nicely using private.
If, further down the line, you're going to add data models and a service layer to your project, there's a good change you'll write an (abstract) parent class, if only for type-hinting.
If those service instances contain something like a config property, you'll probably define that getter in the parent class (to only define it once). private means that only the current class has access to a property, but since you're not going to have an instance of the parent to work with, but an instance of the child, you'll see why protected is invaluable when dealing with larger projects, too.
As far as temporary variables are concerned, be it in methods, functions or anywhere else, you don't have to predeclare them, except for, in certain cases arrays:
public function foo()
{
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
Is perfectly valid, and wouldn't work any better if you were to write
public function foo()
{
$temp;// or $temp = null;
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
However, it's not uncommon to see simething like this:
public function bar($length = 1)
{
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
This code relies on PHP being kind enough to create an array, and assign it to $return when the $return[] = rand(); statement is reached. PHP will do so, but setting your ini to E_STRICT | E_ALL will reveal that it doesn't do so without complaining about it. When passing 0 to the method, the array won't be created, and PHP will also complain when it reaches the return $return; statement: undeclared variable. Not only is it messy, it's also slowing you down! You're better off declaring $return as an array at the top of the scope:
public function bar($length = 1)
{
$return = array();//that's it
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
To be on the safe side, I'd also check the argument type:
/**
* construct an array with random values
* #param int $length = 1
* #return array
**/
public function bar($length = 1)
{
$length = (int) ((int) $length > 0 ? $length : 1);//make length > 0
$return = array();
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
In most if not all cases: yes.
If the variables are class properties they absolutely should be declared before use.
If the variable is local to a function, declare it in that function before you use it. Function variables are confined to the function's scope (local variables). They don't have to be declared before use but it's good practice to do so, and it gets rid of a warning message if you do. If they are not used anywhere else, they should not be properties though,
If you are using it in the context of the whole class, then yes, you should define your variable as a member of the class.
However, if you are talking about a local variable within the context of a single function and the variable does not need to be used elsewhere (or is not returned), then no.
Essentially you need to determine the importance and scope of your variable before deciding whether to make it a class property or not.
For example:
<?php
class Test {
private $test; // Private property, for use in the class only
public $public_test; // Public Property, for use both internally and external to the class as a whole
public function testing() {
$local = 5; // Local variable, not needed outside of this function ever
$this->test = rand(1, 5);
$calc = $local * $this->test; // Local variable, not needed outside of this function ever
$this->public_test = $calc / 2; // The only thing that the whole class, or public use cares about, is the result of the calculation divided by 2
}
}
It's generally a good rule of thumb for variables to define and initialize them before use. That includes not only definition and initial value but also validation and filtering of input values so that all pre-conditions a chunk of code is based on are established before the concrete processing of the data those variables contain.
Same naturally applies to object members (properties) as those are the variables of the whole object. So they should be defined in the class already (by default their value is NULL in PHP). Dynamic values / filtering can be done in the constructor and/or setter methods.
The rule for visibility is similar to any rule in code: as little as necessary (the easy rule that is so hard to achieve). So keep things local, then private - depending if it's a function variable or an object property.
And perhaps keep in the back of your mind that in PHP you can access private properties from within the same class - not only the same object. This can be useful to know because it allows you to keep things private a little bit longer.
For instance, I have the (temporary) result of a calculation. Should I pre-declare this variable?
This is normally a local variable in a function or method. It's defined when it receives the return value of the calculation method. So there is no need to pre-declare it (per-se).
...
function hasCalculation() {
$temp = $this->calculate();
return (bool) $temp;
}
...
If the calculation is/was expensive it may make sense to store (cache) the value. That works easily when you encapsulate that, for example within an object. In that case you'll use a private property to store that value once calculated.
Take these rule with a grain of salt, they are for general orientation, you can easily modify from that, so this is open to extend, so a good way to keep things flexible.
I was looking through the PHP documentation and saw several comments where a variable was initialized outside of a class's constructor, similar to the following:
classMyClass {
private $count = 0;
public function __construct() {
//Do stuff
}
}
In PHP Objects, Patterns, and Practice, the author recommends using constructs only for the initialization of properties, deferring any heavy lifting or complex logic to specialized methods. This tutorial (a quick example that I found on Google) also recommends using constructors to initialize properties: http://www.killerphp.com/tutorials/object-oriented-php/php-objects-page-3.php.
Why would you want to initialize a variable outside the constructor? Is this just sloppy coding, or is there a reason to do something like this? I have to say that until recently, I initialized default values outside the constructor, and there doesn't seem to be any programmatic advantage of one way over the other.
When you initialize a variable outside of the constructor, it must be initialized as a constant. You can't do any operation to initialize it. Thus, the initial value of that member is actually a part of the class signature.
For example, this is invalid:
private $var = $othervar;
private $var = func();
You could do it in the constructor as well, but it would be a bit more verbose and add some clutter to the constructor unless there was some sort of logic going on.
More a comment than an answer, but please elaborate here a little:
Since it is recommended to use constructors for property initialization only,
Who says this and why? I assume the only relates to something else than property definitions with default values.
An answer part:
By default in PHP variables do not need to be defined because variables are then defined when first accessed in a write context. All variables, including undefined ones contain NULL (Demo):
class A {}
$a = new A;
var_dump($a->property); # NULL
Introducing class variables (properties) PHP then allowed to actually define variables. Those still return NULL by default, but they are defined (Demo):
class A {
public $property;
}
$a = new A;
var_dump($a->property); # NULL
In the next step of the evolution, this language construct also allows to specify a constant expression. That is constant because definition is compile-time (not run-time as the when the constructor is invoked). An example (Demo):
class A {
public $property = 'hello';
}
$a = new A;
var_dump($a->property); # string(5) "hello"
As this is compile- but your constructor run-time, I find it hard to compare both feature with another. Also it's not clear why you say that initializing via the constructor is recommended.
Far from sloppy... it's good programming practice. As you would also do in Java/C++, it just sets them up, and then you can do any initialisation in the constructor - usually to sent them to non-defaults as such.
Can I define a class constant inside the class constructor function ?
(based on certain conditions)
That goes against the idea of class constants - they should not be dependent on a specific instance. You should use a variable instead.
However, if you insist on doing this, are very adventurous and can install PHP extensions, you can have a look at the runkit extension that allows to modify classes and their constants at runtime. See this doc: http://www.php.net/manual/en/function.runkit-constant-add.php
I don't think you can.
It wouldn't make sense, either - a class constant can be used in a static context, where there is no constructor in the first place.
You'll have to use a variable instead - that's what they're there for.
Try look here:
http://php.net/manual/en/language.oop5.constants.php
http://php.net/manual/en/language.oop5.static.php
Hope this helps.
As far as standard instance constructors go, there is no way to do this, and as others have pointed out, it wouldn't make sense. These constructors are called per created object instance, at the point they are created. There is no guarantee this constructor would get called before some code tried to access the constant. It also doesn't make sense in that the code would get called over and over again each time a new instance was constructed, whereas a const should only get set once.
It would be nice if PHP either offered some kind of static constructor that let you set the value one time for uninitialized constants, or allowed more types of expressions when defining constants. But these are not currently features of PHP. In 2015 an RFC was made that proposed adding static class constructors, but it is, at the time of me writing this answer, still in the draft status, and has not been modified since 2017.
I think the best alternative for now is to not use constants in this kind of scenario, and instead use static methods that return the value you want. This is very simple in that it only uses the PHP language features as is (not requiring any special extensions), these static methods can be called in the standard way, and you don't need to hack the autoloading process to call some kind of initializer function that sets static variables. The method might need to rely on private static variables in order to make sure the same instance is returned every time, if an object instance is being returned. You would need to write the implementation of this method to be constant like in the sense that it will always return the same thing, but takes advantage of being able to do things you can't do with a constant, like return on object instance or rely on complex expressions or function calls. Here is an example:
final class User
{
/** #var DefinitelyPositiveInt|null */ private static $usernameMaxLength;
public static function getUsernameMaxLengthConst(): DefinitelyPositiveInt
{
if ($usernameMaxLength === null) {
$usernameMaxLength = new DefinitelyPositiveInt(40);
}
return $usernameMaxLength;
}
}
$usernameInput.maxLength = User::getUsernameMaxLengthConst();
This is still not a perfect solution because it relies on the programmer to write these in a constant like way when that is desired (always returning the same value). Also, I don't like that the best place to document the fact that it is a const is in the method name, thus making it even longer to call. I also don't like that you now have to call it as a method instead of just accessing a property, which would be syntactically nicer.
This example is essentially an implementation of a singleton, but sometimes the purpose of a singleton is to be a constant rather than just a singleton. What I mean is, you might want the instance to always exist, and it might be an immutable type (none of the properties are public or mutable, only having methods that return new objects/values).
I am sorry to break it to you but it is not possible in vanilla PHP.
I am not very sure about frameworks or extensions but I am sure that it is not possible in vanilla PHP.
I recommend you to use variables instead.
You still can't, but maybe some of these (progressively weirder) ideas (just ideas, not true solutions) will work for you:
(1) You could use a private property, with a public getter method. The property cannot be modified outside the class, such as constants, but unfortunately it is accessed as a method, not as a constant, so the syntax is not the same.
class aClass{
private $const;
function __construct($const){
$this->const=$const;
}
function const(){
return $this->const;
}
}
$var1=new aClass(1);
echo $var1->const(); //Prints 1
(2) If you really want this value to be accessed as constant from outside, you can use define () inside the constructor. Unfortunately it doesn't get tied to the class or object name (as it do when you use const, using for example myClass::myConst). Furthermore, it only works if you create a single instance of the class. The second object you create is going to throw an error for redefining the constant, because is untied.
class otherClass{
function __construct($const){
define('_CONST',$const);
}
function const(){
return _CONST;
}
}
$var2=new otherClass('2');
echo $var2->const(); //Prints 2
echo _CONST; //Prints 2
#$var3=new aClass('3'); //Notice: Constant _CONST already defined
echo _CONST; //Still prints 2!
(3) Perhaps that last problem can be solved by giving variable names to the constants, related to the object to which they belong. This may be a bit weird... but maybe it works for someone.
class onemoreClass{
private $name;
function __construct($const,$name){
$this->name=$name;
$constname=$this->name."_CONST";
define($constname,$const);
}
function const(){
return constant($this->name.'_CONST');
}
}
$name='var4';
$$name=new onemoreClass(4,$name);
echo $var4->const(); //Prints 4
echo var4_CONST; //Prints 4
$name='var5';
$$name=new onemoreClass(5,$name);
echo $var5->const(); //Prints 5
echo var5_CONST; //Prints 5