11

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.

Hans
  • 528
  • 1
  • 8
  • 29

4 Answers4

8

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;
}
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • PHP objects are the same as C objects. You can define a method in a PHP class, and create an object of that class. You cannot define a method in a C struct. Well, C++ class more like. I don't think C has classes per se.. – Maxim Kumpan Jul 17 '13 at 09:42
  • @MaximKumpan: That's not what I'm saying. I'm saying that objects in PHP (which is written in C) are _"translated"_ to a struct, and the methods are functions that have access to a pointer to that struct (`$this` is that pointer). Look into the hackers guide to the Zend Engine for details – Elias Van Ootegem Jul 17 '13 at 09:50
  • Ah. Sorry, I misunderstood the analogy. – Maxim Kumpan Jul 17 '13 at 10:02
  • @MaximKumpan: It's not an analogy: [it's just how it is](https://wiki.php.net/internals/engine/objects), the definition of a PHP object is a `struct`. – Elias Van Ootegem Jul 17 '13 at 10:05
  • 1
    No.No.No. this isn't really right. The object is a struct, but the objects' property table isn't a struct. It's a HashTable (some type of a list). The performance is lost when executing opcodes for assigning variables, not in the variable assignment itself. But there are really made as many malloc's (no reallocs) to the properties' table when you define at compile time as when you define at run time. – bwoebi Jul 17 '13 at 10:18
  • From an explanation standpoint, telling the guy "Hey, a php object is a struct!" is just plain wrong. It might be coded as a struct for lack of a class in plain C, but when you are telling someone about using objects, the analogy should be with a C++ class, not a C struct. You're not going to explain the question using assembler source code, are you? – Maxim Kumpan Jul 17 '13 at 10:24
  • @MaximKumpan: Comparing PHP object to C++ objects is, IMO, equally wrong. I based my saying _"a PHP class is a struct"_ on the definition given on the linked page: _"Internally, the word object is overloaded to also mean “references”"_. A `struct` in C++ can contain functions, too. Structs are, essentially all-public objects anyway. I'm not going all assembler here, just saying that PHP isn't _truly_ OO, as C++ is. BTW: you _can_ write [OO in C](http://www.planetpdf.com/codecuts/pdfs/ooc.pdf), too. That doesn't make it an OOP language – Elias Van Ootegem Jul 17 '13 at 11:11
  • @bwoebi: Fair enough. I know I cut a few corners. Perhaps you can point me in the direction of more detailed documentation on the matter. It's been a long time since I first started to unravel the mysteries of the Zend Engine. I will delete this answer once I have enough material to get a better understanding, or I will edit once I fully understand what my answer is missing – Elias Van Ootegem Jul 17 '13 at 11:13
  • Functions? In structs? [Seriously](http://stackoverflow.com/questions/9871119/define-functions-in-structs)? – Maxim Kumpan Jul 17 '13 at 11:49
  • @EliasVanOotegem just looked at the code again. http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_object_handlers.c#422 and http://lxr.php.net/xref/PHP_TRUNK/Zend/zend_object_handlers.c#zend_get_property_info_quick These two functions are the standard functions for accessing data. I see there that the properties which are defined at compile time are stored in an array (`zend_object->properties_table`) which provides ceratinly a fast access (O(1)). The properties defined at run time are stored in a HashTable (`zend_object->properties`) which is accessible in O(n). So you should predeclare properties! – bwoebi Jul 17 '13 at 11:50
  • @MaximKumpan then call it function pointers: this is the way OO in C works. – bwoebi Jul 17 '13 at 11:51
  • @EliasVanOotegem I've edited a bit, think it's better now. The access itself after having gotten the property_info is O(1) for compile time defined properties; but getting the property_info is a _slow_ HashTable lookup which can (in case of collisions) be O(n). – bwoebi Jul 17 '13 at 13:03
  • @bwoebi: Ok, thanks. I've read some more on the object internals, too. I thought it might be a O(n) lookup (well, theoretical max of O(n)). again: Thanks for your much valued input/edits/time/effort... – Elias Van Ootegem Jul 17 '13 at 13:15
4

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,

Gordon
  • 312,688
  • 75
  • 539
  • 559
Bojangles
  • 99,427
  • 50
  • 170
  • 208
  • 2
    *"Must"* is strong word, *absolutely should be* fits better. – deceze Jul 17 '13 at 09:40
  • @Bojangles Thanks, so what is the golden rule. When are variables Properties of a class and when are they not? – Hans Jul 17 '13 at 09:49
  • When you define a variable in a class scope (so `class Foo { private $bar }`) it's a class property. If it's in either a standalone function or a class method it's a local variable – Bojangles Jul 17 '13 at 09:50
3

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
    }

}
Rudi Visser
  • 21,350
  • 5
  • 71
  • 97
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.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • How can you "access private properties from within the same class - not only the same object"? The manual http://php.net/manual/en/language.oop5.properties.php only mentions usage of `$this` inside class methods _when calling from instance_ but not just from class. Is this what you mean? – Dmitri Zaitsev Sep 15 '13 at 18:30
  • @DmitriZaitsev: If you have to object of class `A` - `$a1` and `$a2` - then the code of a method of `$a1` can access a private property of `$a2`. This is what I mean. All object of the same class can access the private members. – hakre Sep 17 '13 at 05:48
  • 1
    Hm... wouldn't it break encapsulation? Isn't `private` property of `$a2` meant to be out of reach for `$a1`? I don't even see how this can work. If `$a1` calls `$this->privateProperty` that property is of `$a1`. But if it calls `$a2->privateProperty`, that should fail precisely because the property is private. Am I seeing it wrong? Could you provide an information link? – Dmitri Zaitsev Sep 17 '13 at 12:37