Chapter 2: Variables
Accessing A Variable Dynamically By Name (Variable variables)
Variables can be accessed via dynamic variable names. The name of a variable can be stored in another variable, allowing it to be accessed dynamically. Such variables are known as variable variables.
To turn a variable into a variable variable, you put an extra $ put in front of your variable.
$variableName = 'foo';
$foo = 'bar';
// The following are all equivalent, and all output "bar":
echo $foo;
echo ${$variableName};
echo $$variableName;
//similarly,
$variableName = 'foo';
$$variableName = 'bar';
// The following statements will also output 'bar'
echo $foo;
echo $$variableName;
echo ${$variableName};
Variable variables are useful for mapping function/method calls:
function add($a, $b) {
return $a + $b;
}
$funcName = 'add';
echo $funcName(1, 2); // outputs 3
This becomes particularly helpful in PHP classes:
class myClass {
public function __construct() {
$functionName = 'doSomething';
$this->$functionName('Hello World');
}
private function doSomething($string) {
echo $string; // Outputs "Hello World"
}
}
It is possible, but not required to put $variableName between {}:
${$variableName} = $value;
The following examples are both equivalent and output “baz”:
$fooBar = 'baz';
9$varPrefix = 'foo';
echo $fooBar; // Outputs "baz"
echo ${$varPrefix . 'Bar'}; // Also outputs "baz"
Using {} is only mandatory when the name of the variable is itself an expression, like this:
${$variableNamePart1 . $variableNamePart2} = $value;
It is nevertheless recommended to always use {}, because it’s more readable.
While it is not recommended to do so, it is possible to chain this behavior:
$$$$$$$$DoNotTryThisAtHomeKids = $value;
It’s important to note that the excessive usage of variable variables is considered a bad practice by many
developers. Since they’re not well-suited for static analysis by modern IDEs, large codebases with many
variable variables (or dynamic method invocations) can quickly become difficult to maintain.
Dierences between PHP5 and PHP7
Another reason to always use {} or (), is that PHP5 and PHP7 have a slightly dierent way of dealing with dynamic variables, which results in a dierent outcome in some cases.
In PHP7, dynamic variables, properties, and methods will now be evaluated strictly in left-to-right order, as opposedto the mix of special cases in PHP5. The examples below show how the order of evaluation has changed.
Case 1 : $$foo[‘bar’][‘baz’]
PHP5 interpretation : ${$foo[‘bar’][‘baz’]}
PHP7 interpretation : ($$foo)[‘bar’][‘baz’]
Case 2 : $foo->$bar[‘baz’]
PHP5 interpretation : $foo->{$bar[‘baz’]}
PHP7 interpretation : ($foo->$bar)[‘baz’]
Case 3 : $foo->$bar[‘baz’]()
PHP5 interpretation : $foo->{$bar[‘baz’]}()
PHP7 interpretation : ($foo->$bar)[‘baz’]()
Case 4 : Foo::$bar[‘baz’]()
PHP5 interpretation : Foo::{$bar[‘baz’]}()
PHP7 interpretation : (Foo::$bar)[‘baz’]()
Data Types
There are dierent data types for dierent purposes. PHP does not have explicit type denitions, but the type of a variable is determined by the type of the value that is assigned, or by the type that it is casted to. This is a brief overview about the types, for a detailed documentation and examples, see the PHP types topic.
There are following data types in PHP: null, boolean, integer, oat, string, object, resource and array.
Null
Null can be assigned to any variable. It represents a variable with no value.
$foo = null;
This invalidates the variable and it’s value would be undened or void if called. The variable is cleared from memory and deleted by the garbage collector.
Boolean
This is the simplest type with only two possible values.
$foo = true;
$bar = false;
Booleans can be used to control the ow of code.
$foo = true;
if ($foo) {
echo "true";
} else {
echo "false";
}
Integer
An integer is a whole number positive or negative. It can be in used with any number base. The size of an integer is platform-dependent. PHP does not support unsigned integers.
$foo = -3; // negative
$foo = 0; // zero (can also be null or false (as boolean)
$foo = 123; // positive decimal
$bar = 0123; // octal = 83 decimal
$bar = 0xAB; // hexadecimal = 171 decimal
$bar = 0b1010; // binary = 10 decimal
var_dump(0123, 0xAB, 0b1010); // output: int(83) int(171) int(10)
Float
Floating point numbers, “doubles” or simply called “oats” are decimal numbers.
$foo = 1.23;
$foo = 10.0;
$bar = -INF;
$bar = NAN;
Array
An array is like a list of values. The simplest form of an array is indexed by integer, and ordered by the index, with the first element lying at index 0.
$foo = array(1, 2, 3); // An array of integers
$bar = ["A", true, 123 => 5]; // Short array syntax, PHP 5.4+
echo $bar[0]; // Returns "A"
echo $bar[1]; // Returns true
echo $bar[123]; // Returns 5
PHP Notes for Professionals 11echo $bar[1234]; // Returns null
Arrays can also associate a key other than an integer index to a value. In PHP, all arrays are associative arrays behind the scenes, but when we refer to an ‘associative array’ distinctly, we usually mean one that contains one or more keys that aren’t integers.
$array = array();
$array["foo"] = "bar";
$array["baz"] = "quux";
$array[42] = "hello";
echo $array["foo"]; // Outputs "bar"
echo $array["bar"]; // Outputs "quux"
echo $array[42]; // Outputs "hello"
String
A string is like an array of characters.
$foo = "bar";
Like an array, a string can be indexed to return its individual characters:
$foo = "bar";
echo $foo[0]; // Prints 'b', the first character of the string in $foo.
Object
An object is an instance of a class. Its variables and methods can be accessed with the -> operator.
$foo = new stdClass(); // create new object of class stdClass, which a predefined, empty class
$foo->bar = "baz";
echo $foo->bar; // Outputs "baz"
// Or we can cast an array to an object:
$quux = (object) ["foo" => "bar"];
echo $quux->foo; // This outputs "bar".
Resource
Resource variables hold special handles to opened oles, database connections, streams, image canvas areas and the like (as it is stated in the manual).
$fp = fopen('file.ext', 'r'); // fopen() is the function to open a file on disk as a resource.
var_dump($fp); // output: resource(2) of type (stream)
To get the type of a variable as a string, use the gettype() function:
echo gettype(1); // outputs "integer"
echo gettype(true); // "boolean
Global variable best practices
We can illustrate this problem with the following pseudo-code
function foo() {
global $bob;
$bob->doSomething();
}
Your first question here is an obvious one
Where did $bob come from?
Are you confused? Good. You’ve just learned why globals are confusing and considered a bad practice.
If this were a real program, your next bit of fun is to go track down all instances of $bob and hope you find the right one (this gets worse if $bob is used everywhere). Worse, if someone else goes and defines $bob (or you forgot and reused that variable) your code can break (in the above code example, having the wrong object, or no object at all, would cause a fatal error).
Since virtually all PHP programs make use of code like include(‘file.php’); your job maintaining code like this becomes exponentially harder the more files you add.
Also, this makes the task of testing your applications very difficult. Suppose you use a global variable to hold your database connection:
$dbConnector = new DBConnector(…); function doSomething() { global $dbConnector;
$dbConnector->execute(“…”); }
In order to unit test this function, you have to override the global $dbConnector variable, run the tests and then reset it to its original value, which is very bug prone:
/** * @test */ function testSomething() { global $dbConnector; $bkp = $dbConnector; // Make backup
$dbConnector = Mock::create(‘DBConnector’); // Override assertTrue(foo()); $dbConnector = $bkp; // Restore }
How do we avoid Globals?
The best way to avoid globals is a philosophy called Dependency Injection. This is where we pass the tools we need into the function or class.
function foo(\Bar $bob) {
$bob->doSomething();
}
This is much easier to understand and maintain. There’s no guessing where $bob was set up because the caller is responsible for knowing that (it’s passing us what we need to know). Better still, we can use type declarations to restrict what’s being passed.
So we know that $bob is either an instance of the Bar class, or an instance of a child of Bar, meaning we know we can use the methods of that class. Combined with a standard autoloader (available since PHP 5.3), we can now go track down where Bar is defined. PHP 7.0 or later includes expanded type declarations, where you can also use scalar types (like int or string).
Version = 4.1
Superglobal variables
Super globals in PHP are predefined variables, which are always available, can be accessed from any scope
throughout the script.
There is no need to do global $variable; to access them within functions/methods, classes or files.
These PHP superglobal variables are listed below:
- $GLOBALS
- $_SERVER
- $_REQUEST
- $_POST
- $_GET
- $_FILES
- $_ENV
- $_COOKIE
- $_SESSION
Default values of uninitialized variables
Although not necessary in PHP however it is a very good practice to initialize variables. Uninitialized variables have a default value of their type depending on the context in which they are used:
Unset AND unreferenced
var_dump($unset_var); // outputs NULL
Boolean
echo($unset_bool ? "true\n" : "false\n"); // outputs 'false'
String
$unset_str .= 'abc';
var_dump($unset_str); // outputs 'string(3) "abc"'
Integer
Integer
$unset_int += 25; // 0 + 25 => 25
var_dump($unset_int); // outputs 'int(25)'
Float/double
$unset_float += 1.25;
var_dump($unset_float); // outputs 'float(1.25)'
Array
$unset_arr[3] = "def";
var_dump($unset_arr); // outputs array(1) { [3]=> string(3) "def" }
Object
$unset_obj->foo = 'bar';
var_dump($unset_obj); // Outputs: object(stdClass)#1 (1) { ["foo"]=> string(3) "bar" }
Relying on the default value of an uninitialized variable is problematic in the case of including one file into another which uses the same variable name.
Variable Value Truthiness and Identical Operator
In PHP, variable values have an associated “truthiness” so even non-boolean values will equate to true or false. This allows any variable to be used in a conditional block, e.g.
if ($var == true) { /* explicit version */ }
if ($var) { /* $var == true is implicit */ }
Here are some fundamental rules for different types of variable values:
- Strings with non-zero length equate to true including strings containing only whitepace such as ‘ ‘.
- Empty strings ” equate to false.
$var = '';
$var_is_true = ($var == true); // false
$var_is_false = ($var == false); // true
$var = ' ';
$var_is_true = ($var == true); // true
$var_is_false = ($var == false); // false
- Integers equate to true if they are nonzero, while zero equates to false.
$var = -1;
$var_is_true = ($var == true); // true
$var = 99;
$var_is_true = ($var == true); // true
$var = 0;
$var_is_true = ($var == true); // false
- null equates to false
$var = null;
$var_is_true = ($var == true); // false
$var_is_false = ($var == false); // true
- Empty strings ” and string zero ‘0’ equate to false.
$var = '';
$var_is_true = ($var == true); // false
$var_is_false = ($var == false); // true
$var = '0';
$var_is_true = ($var == true); // false
$var_is_false = ($var == false); // true
- Floating-point values equate to true if they are nonzero, while zero values equates to false.
- NAN (PHP’s Not-a-Number) equates to true, i.e. NAN == true is true. This is because NAN is a nonzero floating-point value.
- Zero-values include both +0 and -0 as defined by IEEE 754. PHP does not distinguish between +0 and -0 in its double-precision floating-point, i.e. floatval(‘0’) == floatval(‘-0’) is true.
- In fact, floatval(‘0’) === floatval(‘-0’).
- Additionally, both floatval(‘0’) == false and floatval(‘-0’) == false.
$var = NAN;
$var_is_true = ($var == true); // true
$var_is_false = ($var == false); // false
$var = floatval('-0');
$var_is_true = ($var == true); // false
$var_is_false = ($var == false); // true
$var = floatval('0') == floatval('-0');
$var_is_true = ($var == true); // false
$var_is_false = ($var == false); // true
In the PHP Documentation for Comparison Operators, there is an Identical Operator
This operator can be used to check whether a variable is identical to a reference value:
$var = null;
$var_is_null = $var === null; // true
$var_is_true = $var === true; // false
$var_is_false = $var === false; // false
It has a corresponding not identical operator
$var = null;
$var_is_null = $var !== null; // false
$var_is_true = $var !== true; // true
$var_is_false = $var !== false; // true
The identical operator can be used as an alternative to language functions like is_null().
USE CASE WITH strpos()
The strpos($haystack, $needle) language function is used to locate the index at which $needle occurs in
$haystack, or whether it occurs at all. The strpos() function is case sensitive; if case-insensitive find is what you need you can go with stripos($haystack, $needle)
The strpos & stripos function also contains third parameter offset (int) which if specified, search will start this number of characters counted from the beginning of the string. Unlike strrpos and strripos, the offset cannot be negative
The function can return:
- 0 if $needle is found at the beginning of $haystack;
- a non-zero integer specifying the index if $needle is found somewhere other than the beginning in $haystack;
- and value false if $needle is not found anywhere in $haystack.
Because both 0 and false have truthiness false in PHP but represent distinct situations for strpos(), it is
important to distinguish between them and use the identical operator
===
to look exactly for false and not just a value that equates to false.
$idx = substr($haystack, $needle);
if ($idx === false)
{
// logic for when $needle not found in $haystack
}
else
{
// logic for when $needle found in $haystack
}
Alternatively, using the not identical operator:
$idx = substr($haystack, $needle);
if ($idx !== false)
{
// logic for when $needle found in $haystack
}
else
{
// logic for when $needle not found in $haystack
}