PHP Basics - Zend PHP 5 Certification Study Guide (2014)

Zend PHP 5 Certification Study Guide (2014)

PHP Basics

Every PHP application is forged from a small set of basic construction blocks. From its very inception, PHP was built with the intent of providing simplicity and choice—and this is clearly reflected in the number of options available in building applications. In this chapter we will cover the essentials that you will use day in, day out.

Syntax

PHP’s syntax is derived from many languages—predominantly from the C language, but Perl has also had a significant influence on its syntax. With the latest object-oriented additions, more Java-like syntax is creeping in as well. Despite incorporating elements of so many other languages, PHP’s syntax remains simple and easy to understand.

Source Files and PHP Tags

Even though it is often used as a pure language, PHP is primarily designed as a text processor (hence its name). To facilitate this role, PHP code can be inserted directly into a text file using a special set of tags; the interpreter will then output any text outside the tags as-is, and execute the code that is between the tags.

There are five types of tags available:

Standard Tags

<?php

... code

?>

Standard tags are the de-facto opening and closing tags; they are the best solution for portability and backwards compatibility, because they are guaranteed to be available and cannot be disabled by changing PHP’s configuration file.

Echo Tags

<?= $variableToEcho; ?>

Prior to PHP 5.4, enabling short tags also made available the short-form <?= $variable; ?> syntax, also known as “echo tags”, which allows you to print the result of an expression directly to the script’s output. With the release of PHP 5.4, short tags and echo tags were split, and echo tags are nowalways enabled.

Short Tags

<?

... code

?>

Short tags were, for a time, the standard in the PHP world; however, they do have the major drawback of conflicting with XML processing instructions (e.g. <?xml) and, therefore, are no longer recommended and have somewhat fallen by the wayside.

Script Tags

<script language="php">

... code

</script>

Script tags were introduced so that HTML editors that were able to ignore JavaScript but were unable to cope with the standard PHP tags could also ignore PHP code.

ASP Tags

<%

... code

%>

Nobody quite understands why ASP tags were introduced; however, if you are so inclined, you can turn on this optional configuration option, and you are then free to use them.

Short tags, script tags and ASP tags are all considered deprecated and their use is strongly discouraged.

Newline Characters

It is important to remember that every character outside of PHP tags is copied as-is by the interpreter to the script’s output—and this includes newline characters.

Newlines are normally ignored by browsers, as they are non-semantic characters in HTML. However, they are also used as separators between the header portion of a web server’s HTTP response and the actual data; therefore, outputting a newline character before all of the headers have been written to the output can cause some rather unpleasant (and unintended) consequences. To mitigate this problem, the first newline directly after a closing tag (?> only) is stripped by the parser. Doing so also solves a problem introduced by the fact that a number of popular text editors will automatically append a newline to the end of your file, thus interfering with include files, which are not supposed to output any text.

An easy way to prevent spurious output from an include file is to omit the closing tag at the end, which the parser considers perfectly legal.

Anatomy of a PHP Script

Every PHP script is made up of statements, such as function calls, variable assignments, data output, directives, and so on. Except in very few cases, each of these instructions must be terminated–just like in C, Perl and JavaScript–with a semicolon. This requirement is not always strict—for example, the last instruction before a closing tag does not require a semicolon. However, such exceptions should really be considered quirks in the parser’s logic, and you should always terminate your instructions with a semicolon:

some_instruction();

$variable = 'value';

Comments

Another common part of any programming language is comments. It is good programming practice to comment every function, class, method or property in your code (although you will likely come across lots of code that is poorly commented—or not commented at all). Remember: any code that took thought to write will take thought to re-read after several days, months or, in some cases, years.

As with tags, PHP gives you multiple choices for your comments:

Listing 1.1: Comments

// Single line comment

# Single line comment

/* Multi-line

comment

*/

/**

* API Documentation Example

*

* @param string $bar

*/

function foo($bar) { }

Both types of single-line comments, // and #, can be ended using a newline (\r, \n or \r\n) or by ending the current PHP block using the PHP closing tag: ?>.

Because the closing tag ?> will end a comment, code like // Do not show this ?> or this will output or this, which is not the intended behavior.

Whitespace

Finally, we reach a subject with very little substance (pun definitely intended): whitespace. PHP is whitespace-insensitive except in a few key areas. This means that there are no requirements to use (or not to use) a specific type of whitespace character (e.g.: tabs rather than spaces), a particular number of whitespace characters, or even to have consistent spacing. However, there are a few limitations:

· You cannot have any whitespace between <? and php

· You cannot break apart keywords (e.g.: whi le, fo r, and funct ion)

· You cannot break apart variable names and function names (e.g.: $var name and function foo bar())

· Heredoc and Nowdoc closing identifiers must not be preceded by anything, including whitespace.

Nowdoc is a new feature of PHP 5.3; you can read more about it in the Strings chapter.

Code Blocks

A code block is simply a series of statements enclosed between two braces:

{

// Some comments

f(); // a function call

}

Code blocks are handy for creating groups of script lines that must all be executed under specific circumstances, such as a function call or a conditional statement. Code blocks can be nested.

For a code block to be useful, you would typically precede it with a statement type identifier: function, for, foreach, do, while, if, else, elseif, or switch.

Language Constructs

Language constructs are elements that are built into the language and, therefore, follow special rules. Perhaps the most common of them is the echo statement, which allows you to write data to the script’s output:

echo "A string to output"; // will output "A string to output"

The echo construct may optionally use parentheses, but they are not required, and in fact must be omitted when you pass more than one argument (arguments are comma-separated, just as with regular functions).

It’s important to understand that echo is not a function and, as such, it does not have a return value. If you need to output data as part of a more complex expression, you can use print() instead, which, whilst also a language construct, behaves like a function, as it has a return value (which is always 1).

// will output: String 1String 2 (note: no space)

echo "String 1", "String 2";

// will output: A string, and return 1

print ("A string");

Another very important construct is die(), which is an alias of exit(). It allows you to terminate the script and either output a string or return a numeric status value to the process that called the script.

Functions are, obviously, an important element of the PHP language. As such, they are covered in their own eponymous chapter.

Data Types

PHP supports many different data types, but they are generally divided into two categories: scalar and composite.

A scalar data type contains only one value at a time. PHP supports four scalar types:

Data Type

Description

boolean

A value that can only be either true or false

int

A signed numeric integer value

float

A signed floating-point value

string

A collection of binary data

Numeric Values

PHP recognizes two types of number: integer and floating-point values. The int data type is used to represent signed integers (meaning that both positive and negative numbers can be expressed with it). Numbers can be declared using several different notations:

Notation

Example

Description

Decimal

10; -11; 1452

Standard decimal notation. Note that no thousand separator is needed (or, indeed, allowed).

Octal

0666, 0100

Octal notation—identified by its leading zero and used mainly to express UNIX-style access permissions.

Hexadecimal

0x123; 0XFF; -0x100

Base-16 notation; note that the hexadecimal digits and the leading 0x prefix are both case-insensitive.

Binary

0b011; 0B010101; -0b100

Base-2 notation; note that the zero is followed by a case-insensitive B, and, of course, only 0s and 1s are allowed.

Note: All non-decimal numbers are converted to decimal when output with echo or print.

It is important that you are well aware of the different notations—in particular, octal numbers can easily be confused with decimal numbers, which can lead to some… interesting consequences!

Floating-point numbers (also called floats or, sometimes, doubles) are numbers that have a fractional component; like integers, they are also signed. PHP supports two different notations for expressing them:

Notation

Example

Description

Decimal

0.12; 1234.43; -.123

Traditional decimal notation.

Exponential

2E7, 1.2e2

Exponential notation—a set of significant digits (also called the mantissa) followed by the case-insensitive letter E and an exponent. The resulting number is expressed multiplied by ten to the power of the exponent—for example, 1e2 equals 100.

There are a few important gotchas that you need to be aware of when dealing with numbers. First of all, the precision and range of both types varies depending on the platform on which your scripts run. For example, 64-bit platforms may, depending on how PHP was compiled, be capable of representing a wider range of integer numbers than 32-bit platforms. What’s worse, PHP doesn’t track overflows, so that the result of a seemingly innocuous operation such as an addition can have catastrophic consequences on the reliability of your application.

Most importantly, you need to be aware that the float data type is not always capable of representing numbers in the way you expect it to. Consider, for example, this very simple statement:

echo (int) ((0.1 + 0.7) * 10);

You would expect that the expression ((0.1 + 0.7) * 10) would evaluate to 8 (and, in fact, if you print it out without the integer conversion, it does). However, the statement above outputs 7 instead. This happens because the result of this simple arithmetic expression is stored internally as 7.999999instead of 8; when the value is converted to int, PHP simply truncates away the fractional part, resulting in a rather significant error (12.5%, to be exact).

The lesson that you need to take home from all this is simple: know the limitations of your numeric data types, and plan around them. Whenever the precision of your calculation is a relevant factor to the proper functioning of your application, you should consider using the arbitrary precision functions provided by the BCMath extension (http://php.net/manual/en/book.bc.php) instead of PHP’s built-in data types.

Strings

In the minds of many programmers, strings are equivalent to text. While in some languages this is, indeed, the case, in many others (including PHP), this would be a very limiting-and, in some cases, incorrect-description of this data type. Strings are, in fact, ordered collections of binary data—this could be text, but it could also be the contents of an image file, a spreadsheet, or even a music recording.

PHP provides a vast array of functionality for dealing with strings. As such, we have dedicated a whole chapter to them—entitled, quite imaginatively, Strings.

Booleans

A Boolean datum can only contain one of two values: true or false. Generally speaking, booleans are used as the basis for logical operations, which are discussed later in this chapter.

When converting data to and from the boolean type, several special rules apply:

· A number (either integer or floating-point) converted into a boolean becomes false if the original value is zero, or true otherwise.

· A string is converted to false only if it is empty or if it contains the single character 0. If it contains any other data–even multiple zeros–it is converted to true.

· When converted to a number or a string, a boolean becomes 1 if it is true, or 0 otherwise.

Compound Data Types

In addition to the scalar data type that we have just examined, PHP supports two compound data types—so called because they are essentially containers of other data:

· Arrays are containers of ordered data elements; an array can be used to store and retrieve any other data type, including numbers, boolean values, strings, objects and even other arrays. They are discussed in the Arrays chapter.

· Objects are containers of both data and code. They form the basis of Object-oriented Programming, and are also discussed in a separate chapter called Object Oriented Programming in PHP.

Other Data Types

In addition to the data types that we have seen so far, PHP defines a few additional types that are used in special situations:

· NULL indicates that a variable has no value. A variable is considered to be NULL if it has been assigned the special value NULL, or if it has not yet been assigned a value at all—although, in the latter case, PHP may output a warning if you attempt to use the variable in an expression.

· The resource data type is used to indicate external resources that are not used natively by PHP, but that have meaning in the context of a special operation—such as, for example, handling files or manipulating images.

Converting Between Data Types

As we mentioned, PHP takes care of converting between data types transparently when a datum is used in an expression. However, it is still possible to force the conversion of a value to a specific type using type conversion operators, also referred to as type casting. This is simply the name of the data type to which you want to convert, enclosed in parentheses and placed before an expression. For example:

$x = 10.88;

echo (int) $x; // Outputs 10

Note that a value cannot be converted to some special types; for example, you cannot convert any value to a resource. You can, however, convert a resource to a numeric or string data type, in which case PHP will return the numeric ID of the resource, or the string Resource id # followed by the resource ID.

Variables

Variables are temporary storage containers. In PHP, a variable can contain any type of data, such as, for example, strings, integers, floating-point numbers, objects and arrays. PHP is loosely typed, meaning that it will implicitly change the type of a variable as needed, depending on the operation being performed on its value. This contrasts with strongly typed languages, like C and Java, where variables can only contain one type of data throughout their existence.

PHP variables are identified by a dollar sign $ followed by an identifier name. Variables must be named using only letters (a-z, A-Z), numbers and the underscore character; their names must start with either a letter or an underscore. Variable names are one of only two identifier types in PHP that are case-sensitive (the other is constants, discussed below). Here are a few examples:

Technically, the letters allowed in variable names include the bytes 127 through 255 (0x7f-0xff) but in practice they are seldom used.

$name = 'valid'; // Valid name

$_name = 'valid'; // Valid name

$1name = 'invalid'; // Invalid name, starts with a number

Variables can also be interpolated-that is, inserted-directly into certain types of strings. This is described in the Strings chapter.

Type Casting

While PHP is loosely typed, and will automatically coerce types for things like comparisons, variables do have a type and can be cast between types.

Remember from earlier that PHP supports integers, floats, booleans, strings, arrays, objects, and resources, as well as the NULL value.

The most common way to cast is to use the casting operator, which consists of the type to cast to inside of parentheses:

$string = (string) 123; // String: "123"

When casting simple types (integers, floats, booleans, and strings) to arrays you will end up with an array whose first key (0) will be the original value.

When casting between arrays and objects, array keys will become object properties, and object properties will become array keys.

Casting to an object will result in a new stdClass object.

Listing 1.2: Casting to an object

$obj = (object) ["foo" => "bar", "baz" => "bat"];

var_dump($obj)

/* Results in:

class stdClass#186 (2) {

public $foo =>

string(3) "bar"

public $baz =>

string(3) "bat"

}

*/

In addition to the casting operator there are several functions that can perform the same task:

Function

Result

intval()

cast the given variable to an integer

floatval()

cast the given variable to a float

strval()

cast the given variable to a string

boolval()

cast the given variable to a boolean. Added in PHP 5.5.

settype()

cast the given variable to a given type

There are no functions for casting to objects or arrays.

Detecting Types

PHP also provides functions to detect variable types. These take the form of is_<type>():

Function

Result

is_int()

checks for integers

is_float()

checks for floats

is_string()

checks for strings

is_bool()

checks for booleans

is_null()

checks for nulls

is_array()

checks for arrays

is_object()

checks for objects

These will only return true if the variable is of the type specified; for example, the function is_int() will return false when passed the string "123".

There is, however, a special method, is_numeric(), which will return true if passed any variable that is a number-this includes integers, floats, and strings starting with integers or floats.

Variable Variables

In PHP, it is also possible to create so-called variable variables. That is a variable whose name is contained in another variable. For example:

$name = 'foo';

$$name = 'bar';

echo $foo;

// Displays 'bar'

As you can see, in this example we start by creating a variable that contains the string foo. Next, we use the special syntax $$name, starting with two dollar signs, to indicate that we want the interpreter to use the contents of $name to reference a new variable—thus creating the new variable $foo, which is then printed out normally.

Because of the availability of variable variables, it is indeed possible to create variables whose names do not follow the constraints listed above. This is also possible by defining the name between braces:

Listing 1.3: Circumventing naming constraints

$name = '123';

/* 123 is your variable name, this would normally be invalid. */

$$name = '456';

// Again, you assign a value.

echo ${'123'};

// Finally, using curly braces you can output '456'

Variable variables are a very powerful tool and should be used with extreme care, not only because they can make your code difficult to understand and document, but also because their improper use can lead to some significant security issues.

Inspecting Variables

There are several ways to inspect variables in PHP, but only one main reason: debugging.

The first, and simplest way, is print_r():

Listing 1.4: Using print_r

$array = array(

'foo',

'bar',

'baz',

);

print_r($array);

$obj = new StdClass();

$obj->foo = "bar";

$obj->baz = "bat";

print_r($obj);

This outputs the following:

Array

(

[0] => foo

[1] => bar

[2] => baz

)

stdClass Object

(

[foo] => bar

[baz] => bat

)

As you can see, it simply states that it’s an array, or the object type, and lists the keys or properties and their values.

With strings, integers and floats, the value is simply output. With booleans and null it gets a little tricky: for false or null, nothing is output, for true, a 1 is output.

In truth, print_r() provides little more than echo.

A better option, and my personal favorite, is var_dump(). var_dump() is very similar, except that in addition to displaying the data, it also displays the type and the length. This is especially useful for finding non-printable characters in strings, as well as differentiating between empty strings, falseand null.

Finally, you can pass in any number of arguments, and it will dump them all.

var_dump(null, false, "", 1, 2.3,

array("foo", "bar", "baz" => 1.23)

);

Outputs:

NULL

bool(false)

string(0) ""

int(1)

float(2.3)

array(3) {

[0]=>

string(3) "foo"

[1]=>

string(3) "bar"

["baz"]=>

float(1.23)

}

When the xdebug extension is installed, by default it will output pretty HTML var_dump() output when the html_errors php.ini setting is enabled. This is a great tool for quickly reading this information, especially as browsers ignore the real whitespace shown in the output above. Xdebug even provides configuration settings for the maximum depth, length, and number of children to display, which is useful when your data structures are very large.

Another option for debugging is debug_zval_dump(). This outputs the internal engine representation of a variable. However, this function is rarely used.

The final inspecting option, is var_export(). This allows you to output a syntactically valid string representation of a variable, which if you were to execute it with PHP, would re-create the same variable value.

Listing 1.5: Using var_export

$array = array(

"foo" => "bar",

"baz" => "bat"

);

var_export($array);

$obj = new StdClass();

$obj->foo = "bar";

$obj->baz = "bat";

var_export($obj);

Outputs:

array (

'foo' => 'bar',

'baz' => 'bat',

)

stdClass::__set_state(array(

'foo' => 'bar',

'baz' => 'bat',

))

This, when executed, would re-create the original array or object and could be assigned to a variable.

Be aware that var_export() does not end its output with a semi-colon!

All of these functions can also be used to examine constants, though in the case of var_export() you might not get the result you were expecting.

Determining If a Variable Exists

One of the downsides of the way PHP handles variables is that there is no way to ensure that any one of them will exist at any given point in the execution of a script. This can introduce a range of problems—from annoying warnings, if you try to output the value of a non-existent variable, to significant security and functionality issues when variables are unexpectedly unavailable when you need them.

To mitigate this problem, you can use the special construct isset():

if (isset($x)) {

// Do something if $x is set

} elseif (!isset($x)) {

// Do something if $x is not set

}

A call to isset() will return true if a variable exists and has a value other than NULL.

Determining If a Variable is Empty

In addition to isset(), PHP also has another special construct: empty().

empty() will return true if the value passed to it is NULL, or any value that can be coerced to false, including an empty string, an empty array, integer zero, and string “0”.

$variable = "0";

if (empty($variable)) {

// true

}

Prior to PHP 5.5, empty() only accepted variables for its argument; from PHP 5.5, it will accept any valid expression:

if (empty(someFunction())) {

...

}

Constants

Conversely to variables, constants are meant for defining immutable values. Constants can be accessed for any scope within a script; however, they can only contain scalar values. Constant names, like variables, are case-sensitive; they also follow the same naming requirements, with the exception of the leading $. It is considered best practice to define constants using only upper-case names.

Here’s an example of constants at work:

Listing 1.6: Defining constants

define('EMAIL', 'davey@php.net'); // Valid name

echo EMAIL; // Displays 'davey@php.net'

define('USE_XML', true);

if (USE_XML) { } // Evaluates to true

define('1CONSTANT', 'some value'); // Invalid name

From PHP 5.3 you can also use the const keyword to define constants:

const EMAIL = 'davey@php.net';

echo EMAIL;

Also, from PHP 5.6 you can use constant scalar expressions to define the value:

const DOMAIN = "php.net";

const EMAIL = "davey@" . DOMAIN;

Constant scalar expressions support any valid expression that only uses static scalar values such as:

· Integers, Floats and Strings

· Magic constants such as __LINE__, __FILE__, __DIR__, and __METHOD__

· Heredoc (without variables) and Nowdoc strings

· Other constants

Valid expressions include:

· Math

· Bitwise Math

· Boolean Logic

· Concatenation

· Ternary

· Comparisons

Operators

As their name subtly suggests, operators are the catalysts of operations. There are many types of operators in PHP; those most commonly used are:

· Assignment Operators for assigning data to variables

· Arithmetic Operators for performing basic math functions

· String Operators for joining two or more strings

· Comparison Operators for comparing two pieces of data

· Logical Operators for performing logical operations on boolean values

In addition, PHP also provides:

· Bitwise Operators for manipulating bits using boolean math

· Error Control Operators for suppressing errors

· Execution Operators for executing system commands

· Incrementing/Decrementing Operators for adding to and subtracting from numerical values

· Type Operators for identifying Objects

With very few exceptions, PHP’s operations are binary—meaning that they require two operands. All binary operations use an infix notation, in which the operator sits in between its operands (for example, 2 + 2).

Arithmetic Operators

Arithmetic operators allow you to perform basic mathematical operations:

Operator

Example

Description

Addition

$a = 1 + 3.5;

Add the two operands together

Subtraction

$a = 4 - 2;

Subtracts the right operand from the left

Multiplication

$a = 8 * 3;

Multiply the left operand by the right

Division

$a = 15 / 5;

Divide the left operand by the right

Modulus

$a = 23 % 7;

Returns the remainder of the left operand divided by the right

Power

$a = 2 ** 3;

Added in PHP 5.6 Returns the left operand raised to the power of the right.

Do remember that certain arithmetic operators (for example, the addition operator) assume a different meaning when applied to arrays. You can find more information on this subject in the Arrays chapter.

Incrementing/decrementing operators are a special category of operators that make it possible to increment or decrement the value of an integer by one. They are unary operators, because they only accept one operand (that is, the variable that needs to be incremented or decremented), and are somewhat of an oddity, in that their behaviour changes depending on whether they are appended or prepended to their operand.

The position of the operator determines whether the adjustment it performs takes place prior to, or after, returning the value:

· If the operator is placed after its operand, the interpreter will first return the value of the latter (unchanged), and then either increment or decrement it by one.

· If the operator is placed before the operand, the interpreter will first increment or decrement the value of the latter, and then return the newly-calculated value.

Here are a few examples:

Listing 1.7: Incrementing/decrementing operators

$a = 1;

// Assign the integer 1 to $a

echo $a++;

// Outputs 1, $a is now equal to 2

echo ++$a;

// Outputs 3, $a is now equal to 3

echo --$a;

// Outputs 2, $a is now equal to 2

echo $a--;

// Outputs 2, $a is now equal to 1

The excessive use of this operator can make your code hard to understand—even the best programmers have been tripped up at least a few times by a misunderstood increment or decrement operation. Therefore, you should limit your use of these operators and treat them with caution.

It’s important to note that the operand in an increment or decrement operation has to be a variable—using an expression or a hard-coded scalar value will simply cause the parser to throw an error. Also, the variable being incremented or decremented will be converted to the appropriate numeric data type—thus, the following code will return 1, because the string Test is first converted to the integer number 0, and then incremented:

$a = (int) 'Test'; // $a == 0

echo ++$a;

The String Concatenation Operator

Unlike many other languages, PHP has a special operation that can be used to glue—or, more properly, concatenate—two strings together:

Listing 1.8: Concatenate operators

$string = "foo" . "bar";

// $string now contains the value 'foobar'

$string2 = "baz";

// $string2 now contains the value 'baz'

$string .= $string2;

// After concatenating the two variables, we end up

// with 'foobarbaz'. This is shorthand for

// $string = $string . $string2;

echo $string;

// Displays 'foobarbaz'

It is important to remember that this is not just the proper way to concatenate two strings using an operation—it is the only way. Using the addition operator will result in the two strings first being converted into numeric values and then added together (thus also yielding a numeric value).

Bitwise Operators

Bitwise operators allow you to manipulate bits of data. All these operators are designed to work only on integer numbers—therefore, the interpreter will attempt to convert their operands into integers before executing them.

The simplest bitwise operator is bitwise NOT, which negates all the bits of an integer number:

$x = 0;

echo ~$x; // will output -1

A group of binary bitwise operators is used to perform basic bit manipulation by combining the bits of its two operands in various ways:

Bitwise Operator

Description

&

Bitwise AND. The result of the operation will be a value whose bits are set if they are set in both operands, and unset otherwise.

|

Bitwise OR. The result of the operation will be a value whose bits are set if they are set in either operand (or both), and unset otherwise.

^

Bitwise XOR (exclusive OR). The result of the operation will be a value whose bits are set if they are set in either operand, and unset otherwise.

These operations are all quite straightforward—with the possible exception of the exclusive OR, which may look odd at first sight. In reality, its functionality is quite simple: if either the left-hand or right-hand bit is set, the operand behaves in exactly the same as the bitwise OR. If both bits are either set or unset, the resulting bit is unset.

A third set of operators is used to shift bits left or right:

Shift Operator

Description

<<

Bitwise left shift. This operation shifts the left-hand operand’s bits to the left by a number of positions equal to the right operand, inserting unset bits in the shifted positions.

>>

Bitwise right shift. This operation shifts the left-hand operand’s bits to the right by a number of positions equal to the right operand, inserting unset bits in the shifted positions.

It’s interesting to note that these last two operations provide an easy (and very fast) way of multiplying integers by a power of two. For example:

Listing 1.9: Bitwise multiplication

$x = 1;

echo $x << 1; // Outputs 2

echo $x << 2; // Outputs 4

$x = 8;

echo $x >> 1; // Outputs 4

echo $x >> 2; // Outputs 2

You must, however, be aware of the fact that, even though these operations can approximate a multiplication or a division by a power of two, they are not exactly the same thing—in particular, there are overflow and underflow scenarios that can yield unexpected results. For example, on a 32-bit machine, the following will happen:

$x = 1;

echo $x << 32;

echo $x * pow (2, 32);

The second line of this example actually outputs zero—because all the bits have been shifted out of the integer value. On the other hand, the second example (which calls the pow() function to elevate 2 to the power of 32) will return the correct value of 4,294,967,296—which, incidentally, will now be a float because such a number cannot be represented using a signed 32-bit integer.

Assignment Operators

Given the creativity that we have shown in the naming conventions to this point, you’ll probably be very surprised to hear that assignment operators make it possible to assign a value to a variable. The simplest assignment operator is a single equals sign, which we have already seen in previous examples:

$variable = 'value';

// $variable now contains the string 'value'

In addition, it is possible to combine just about every other type of binary arithmetic and bitwise operator with the = sign to simultaneously perform an operation on a variable and reassign the resulting value to itself:

$variable = 1;

// $variable now contains the integer value 1

$variable += 3;

// $variable now contains the integer 4

In this example, we pair the addition operator (the plus sign) with the equals sign to add the existing value of $variable to the right operand, the integer 3. This technique can be used with all binary arithmetic and bitwise operators.

Referencing Variables

By default, assignment operators work by value—that is, they copy the value of one expression to another. If the right-hand operand happens to be a variable, only its value is copied, so that any subsequent change to the left-hand operator is not reflected in the right-hand one. For example:

$a = 10;

$b = $a;

$b = 20;

echo $a; // Outputs 10

Naturally, you expect this to be the case, but there are circumstances in which you may want an assignment to take place by reference, so that the left-hand operand becomes “connected” with the right-hand one:

$a = 10;

$b = &$a; // by reference

$b = 20;

echo $a; // Outputs 20

The assignment operator works by value for all data types except objects, which are always passed by reference, regardless of whether the & operator is used or not.

The use of by-reference variables is a sometimes-useful, but always very risky, technique because PHP variables tend to stay in scope for a long time, even within a single function. Additionally, unlike what happens in many other languages, by-reference activity is often slower than its by-value counterpart, because PHP uses a clever “deferred-copy” mechanism that actually optimizes by-value assignments.

Comparison Operators

Comparison operations are binary operations that establish a relationship of equivalence between two values. They can either establish whether two values are equal (or not equal) to each other, and whether one is greater (or smaller) than the other. The result of a comparison operation is always a boolean value.

There are four equivalence operations:

Equivalence Operators

Description

==

Equivalence. Evaluates to true if the two operands are equivalent, meaning that they can be converted to a common data type in which they have the same value but are not necessarily of the same type.

===

Identity. Evaluates to true only if the operands are of the same data type and have the same value.

!=

Not-equivalent operator. Evaluates to true if the two operands are not equivalent, without regard to their data type.

!==

Not-identical operator. Evaluates to true if the two operands are not of the same data type or do not have the same value.

As you can imagine, it’s easy to confuse the assignment operator = for the comparison operator ==—and this is, in fact, one of the most common programming mistakes. A partial solution to this problem consists of reversing the order of your operands when comparing a variable to an immediate value, often referred to as “Yoda conditions”. For example, instead of writing:

echo $a == 10;

You could write:

echo 10 == $a;

These two operations are completely identical—but, because the left-hand operator of an assignment must be a variable, if you had forgotten one of the equal signs in the second example, the parser would have thrown an error, thus alerting you to your mistake.

A different set of operators establishes a relationship of inequality between two operands—that is, whether one of the two is greater than the other:

Inequality Operators

Description

< and <=

Evaluates to true if the left operand is less than, or less than or equal to, the right operand.

> and >=

Evaluates to true if the left operand is greater than, or greater than or equal to, the right operand.

Clearly, the concept of relationship changes depending on the types of the values being examined. While the process is clear for numbers, things change a bit for other data types; for example, strings are compared by examining the binary value of each byte in sequence until two different values are found; the result of a comparison operation is then determined by the numeric value of those two bytes. For example:

$left = "ABC";

$right = "ABD";

echo (int) ($left > $right);

The code above echoes 0 (that is, false), because the letter D in $right is higher than the corresponding letter C in $left. While you may think that this comparison method is roughly equivalent to alphabetical comparison, this is almost never the case when applied to real-world examples. Consider, for example, the following:

$left = 'apple';

$right = 'Apple';

echo (int) $left > $right;

This example outputs 1 (true), because the ASCII value of the character a (97) is higher than that of the character A (65). Clearly, this approach won’t work well in the context of text comparison, and a different set of functions is required—this is explained in the Strings chapter.

The use of comparison operators with arrays also introduces a different set of rules. These are explained in the Arrays chapter.

Logical Operators

Logical operators are used to connect together boolean values and obtain a third boolean value depending on the first two. There are four logical operators in PHP, of which three are binary. The only unary operator is the Logical NOT, identified by a single exclamation point that precedes its operand:

$a = false;

echo !$a; // outputs 1 (true)

It’s important to understand that all logical operators only work with boolean values; therefore, PHP will first convert any other value to a boolean and then perform the operation.

The three binary operators are:

Binary Operators

Description

&& / and

The AND operator evaluates to true if both the left and right operands evaluate to true. The most commonly-used form of this operator is &&.

|| / or

The OR operator evaluates to true if either the left or right operand evaluates to true, with the || form being more commonly used.

XOR

The Exclusive OR operator evaluates to true if either the left or right operand evaluates to true, but not both.

Note that PHP also has the bitwise operators & and | which should not be confused with the binary operators. The & operator returns the bits that are in both operands while | returns the bits set in either one.

It’s important to understand that PHP employs a very simple shortcut strategy when executing binary logical operations. For example, if the left-hand side operand of an AND operation evaluates to false, then the operation returns false immediately (since any other result would be impossible), without evaluating the right-hand side operand at all.

In addition to improving performance, this approach is a life-saver in many situations where you actually don’t want the right-hand operand to be evaluated at all, based on the first one.

Other Operators

In addition to all the operators we’ve seen so far, PHP also uses a few specialized operators to simplify certain tasks. One of these is the error suppression operator, @; when prepended to an expression, this operator causes PHP to ignore almost all error messages that occur while that expression is being evaluated:

$x = @fopen("/tmp/foo");

The code above will prevent the call to fopen() from outputting an error—provided that the function uses PHP’s own functionality for reporting errors. Sadly, some libraries output their errors directly, bypassing PHP and thereby making it much harder to manage with the error suppression operator.

Use of the @ operator is considered bad practice, because it makes it more difficult to trace and debug errors. It is also slow. While its speed has been greatly improved in PHP 5.3 (and even more so in 5.4), it should be avoided, as it can hide important issues.

If you must use it, you can install the pecl extension xdebug and enable the xdebug.scream option to stop it hiding the errors during development.

The backtick operator makes it possible to execute a shell command and retrieve its output. It is functionally equivalent to calling shell_exec().For example, the following will cause the output of the UNIX ls command to be stored inside $a:

$a = `ls -l`;

Don’t confuse the backtick operator with regular quotes (and, conversely, don’t confuse the latter with the former!).

Operator Precedence and Associativity

As we all learned in school, not all operations have the same precedence. When using an infix notation, the order in which operations are written in an expression lends itself to a certain amount of ambiguity which must, therefore, be resolved. This can be done in one of two ways: by using parentheses to indicate which operations should be performed first, or by using a set of pre-defined precedence rules. In mathematics, these are often referred to as PEMDAS (in the USA) or BODMAS (in the UK).

Even if we establish the precedence of each operation, however, we lack one important tool: how do we decide in which order we execute operations that have the same precedence? This is determined by an operation’s associativity, which can either be left (operations are performed left-to-right), right (operations are performed right-to-left) or none (for operations that cannot be associated).

The following table illustrates the precedence (higher items have higer precedence) and associativity of each operation:

Associativity

Operator

left

[

non-associative

++ --

non-associative

~ - (int) (float) (string) (array) (object) @

non-associative

instanceof

right

!

left

* / % **

left

+ - .

left

<< >>

non-associative

< <= > >=

non-associative

== != === !==

left

&

left

^

left

|

left

&&

left

||

left

? :

right

= += -= *= /= .= %= &= |= ^= <<= >>= **=

left

and

left

xor

left

or

left

,

Note that the logical operators &, &&, and and, as well as |, ||, and or, have different precedence. Mixing their usage in an expression can lead to unintended results.

Control Structures

Control structures allow you to control the flow of your script—after all, if all a script could do was run from start to finish, without any control over which portions of the script are run and how many times, writing a program would be next to impossible.

PHP features a number of different control structures—including some that, despite being redundant, significantly simplify script development. You should be very familiar with all of them, as control structures are a fundamental element of the language.

Conditional Structures

Conditional structures are used to change the execution flow of a script based on one or more conditions. The most basic of these structures is the if-then-else construct, which executes one of two statements (or sets of statements enclosed in a code block) depending on whether a condition evaluates to true or false:

Listing 1.10: If-then-else

if (expression1) {

} elseif (expression2) {

// Note that the space between else and if is optional

} else {

}

Here, if expression1 evaluates to true, the code block immediately following it is executed. Otherwise, the interpreter attempts to execute the contents of the else portion of the statement. Note that you chain together several if-then-else statements by using the elseif construct instead of a simpleelse (you can also use else if, which is equivalent).

Naturally, if-then-else statements can be nested:

Listing 1.11: Nested If-then-else

if (expression1) {

if (expression2) {

// Code

} else {

// More code

}

} else {

if (expression3) {

// More core again.

}

}

A special ternary operator allows you to embed an if-then-else statement inside an expression:

echo 10 == $x ? 'Yes' : 'No';

The code above would be equivalent to the following:

if (10 == $x) {

echo 'Yes';

} else {

echo 'No';

}

As you can see, the former expression is much more concise—and, if used properly, can make code much more readable. However, you should think of this operation as nothing more than a shortcut: used in excess, it can make your code difficult to understand and compromise its functionality, particularly if you start nesting several of these operations into each other.

With PHP 5.3 the ternary operator has become even shorter! It will return the result of the left-hand expression on true if you omit a true value and remove any whitespace between the ? and : like so:

$foo = ($bar) ?: $bat;

Unfortunately, this cannot be used for an if-set-or type of condition, because if the variable does not exist it will issue a notice, and if you use isset(), it will return true on existence, or false otherwise.

The problem with if-then-else statements is that they tend to get rather complicated when you need to check a single expression against several different possible values. Imagine, for example, the not-so-uncommon situation in which you have a series of related if-then-else statements that compare a single variable for multiple possible matches, as in the following:

Listing 1.12: Multiple matches for if-then-else

$a = 0;

if ($a) {

// Evaluates to false

} elseif ($a == 0) {

// Evaluates to true

} else {

// Will only be executed if no other conditions are met

}

There are several problems here. First, you have to write a lot of code, which is difficult to maintain and understand. Second, the value of $a must be evaluated every time an if condition is encountered—which, in this case, is not a big problem, but could be if you needed to evaluate a complex expression. To mitigate this problem, PHP features the switch construct:

Listing 1.13: Switch statement

$a = 0;

switch ($a) { // In this case, $a is the expression

case true: // Compare to true

// Evaluates to false

break;

case 0: // Compare to 0

// Evaluates to true

break;

default:

// Will only be executed if no other conditions

// are met

break;

}

A switch statement evaluates the initial expression ($a in this case) only once, and then compares it against the individual case values; if a match is found, it will continue to execute code until it encounters a break statement. Note that the use of break is required—or the interpreter will continue executing code even if it finds another case. Finally, if none of the test cases match, the interpreter executes the code block in the default block. Once a break is hit, PHP will exit the switch, even if other case values would match.

A great feature of switch is the ability to fall-through by intentionally omitting the break, allowing you to perform the same code for multiple inputs. However, once a case matches, all further case conditions are ignored until (and unless) a break is hit.

Listing 1.14: Switch statement with fall-through

switch ($a) {

case (strpos($a, 'bat') !== false):

echo "The value contains bat" . PHP_EOL;

case (strpos($a, 'foo') !== false):

echo "The value contains foo" . PHP_EOL;

break;

case (strpos($a, 'bar') !== false):

echo "The value contains bar" . PHP_EOL;

break;

}

Depending on the value of $a, this code will have different output. If it contains foo, but not bat, then the output looks like:

The value contains foo

If it contains bat, regardless of the presence of foo, it will be output the following:

The value contains bat

The value contains foo

Unless it only contains bar, and not foo or bat, then the last case is never reached.

Iterative Constructs

Iterative constructs make it possible to execute the same portion of code multiple times. PHP has four of these, although only two of them are necessary to the functioning of a language.

The simplest iterative constructs are the while() and do...while() loops; they allow you to perform a series of operations until a condition evaluates to false:

Listing 1.15: While and Do loops

$i = 0;

while ($i < 10) {

echo $i . PHP_EOL;

$i++;

}

$i = 0;

do {

echo $i . PHP_EOL;

$i++;

} while ($i < 10);

These two types of loop are very similar, with the only difference being the point at which the condition is checked to determine whether the code inside the construct should be executed or not. In a while() loop, the check is performed every time the execution point enters the loop—this means that, if the condition is never true, the code inside the loop will never be executed. In a do...while() loop, on the other hand, the check takes place at the end of each iteration of the loop—meaning that, even if the condition never evaluates to true, the contents of the loop will be executed at least once.

The for and foreach constructs are specialized looping mechanisms that can be used to essentially encapsulate a while() loop that uses a counter in a slightly more readable form:

for ($i = 0; $i < 10;$i++) {

echo $i . PHP_EOL;

}

The for declaration contains three portions, separated by semicolons. The first one contains an instruction (or series of instructions separated by a comma) that is executed once before the loop has begun. The second one contains a condition that is checked at the beginning of every iteration the loop. Finally, the third one contains an instruction (or, again, a set of instructions separated by a comma) that is executed at the end of every iteration. Therefore, the code above would be equivalent to writing the following:

$i = 0;

while ($i < 10) {

echo $i . PHP_EOL;

$i++;

}

The built-in PHP_EOL constant represents the “end of line” marker for your current operating system.

Similar to for is the foreach construct, which allows you to loop through an array or object; we discuss this construct in the Arrays chapter and further in the Object-oriented Design chapter.

Breaking and Continuing

The break keyword, which we encountered briefly in the earlier section about the switch statement, can also be used to immediately exit a loop; it takes an optional parameter, which allows you to exit from multiple nested loops:

Listing 1.16: Breaking out of a loop

$i = 0;

while (true) {

if ($i == 10) {

break;

}

echo $i . PHP_EOL;

$i++;

}

for ($i = 0; $i < 10; $i++) {

for ($j = 0; $j < 3; $j++) {

if (($j + $i) % 5 == 0) {

// Exit from this loop and the next outer one.

break 2;

}

}

}

Remember always to terminate a break statement with a semicolon if it does not have any parameters. If you do not do so, and it is followed by an expression that returns an integer number, you may end up causing the interpreter to randomly exit from more than one loop—causing all sorts of difficult-to-troubleshoot situations.

There are cases in which, rather than terminating a loop, you simply want to skip over the remainder of an iteration and immediately skip over to the next. This is done with the continue statement: as with break, you can provide it with an integer parameter to specify the level of nesting to which it applies. For example, the following code will only output numbers between 0 and 3, and between 6 and 9:

for ($i = 0; $i < 10; $i++) {

if ($i > 3 && $i < 6) {

continue;

}

echo $i . PHP_EOL;

}

Namespaces

PHP 5.3 added support for namespaces—a way to encapsulate code. While you can place any code within a namespace, only classes, traits, interfaces, functions, and constants are affected by them.

In PHP, namespaces allow us to avoid naming collisions and to alias long names caused by avoiding naming collisions.

To use a namespace you must declare it using the namespace keyword. The namespace must be declared at the top of the file. It may only be preceded by the opening PHP tag and a declare statement.

You can then, optionally, surround the contents of the namespace inside of curly braces—but this is not usually done.

namespace Ds;

// Code here is within the namespace

Or, within curly braces:

namespace Ds {

// Code here is within the namespace

}

Namespaces can be declared more than once, allowing you to separate the contents of the namespace across multiple files (e.g. using the one-class-per-file standard, you can have two classes within the same namespace).

You may declare multiple namespaces within a single file; however, this is strongly discouraged. To place code in the global scope, you use the anonymous namespace, but you must use curly braces:

Listing 1.17: Multiple namespaces in a file

namespace Ds {

// Code in the Ds namespace

}

namespace {

// Code in the global space

}

Once code has been namespaced, you can only access it from within that namespace, or with its fully-qualified name.

To define a namespaced constant, use the const keyword.

Sub-Namespaces

You can create sub-namespaces by separating identifiers with the namespace operator: the backslash (\).

namespace Ds\String;

It is not required that all nested namespaces be declared explicitly—meaning that you can have Ds\String\Tools without ever explicitly declaring a Ds\String namespace.

Using Namespaces

We use the same backslash operator to create the fully-qualified name for a namespaced class, interface, trait, function, or constant.

Listing 1.18: A namespaced class

namespace Ds\String;

class Unicode

{

protected $string;

public function __construct($string) {

$this->string = $string;

}

public function strlen() {

if (extension_loaded('iconv')) {

return \iconv_strlen($this->string);

} elseif (extension_loaded('mbstring')) {

return \mb_strlen($this->string);

}

return false;

}

}

Here we have a namespace of Ds\String which contains a Unicode class. The fully-qualified name for our Unicode class is Ds\String\Unicode.

Names are considered relative to the current namespace unless they start with a \. This means that inside the Ds namespace you can use the relative namespace String\Unicode for our class.

When inside a namespace, unless you fully qualify classes and interfaces in the global namespace by prepending them with a \ (e.g. \DateTime), they will be considered relative to the current namespace. However, for functions and constants, the PHP engine will use global functions or constants if one does not exist inside the namespace—unless you have tried to use the function or constant. Let’s look at some examples to illustrate this behavior.

namespace foo;

strlen("foo"); // falls back to \strlen();

However, trying to use it within the namespace will cause a fatal error.

namespace foo;

use function bar\strlen; // doesn't exist

strlen("len"); // doesn't fall back, fatal error:

// Fatal error: Call to undefined function bar\strlen()

When outside a namespace, if you have use \foo\strlen but the function or constant does not exist, it will use the global function or constant without causing any error.

require "foo.php"; // foo namespace

use \foo\strlen; // does not exist

echo strlen('len'); // uses \strlen

To instantiate our class using the fully-qualified name, we do the following:

$string = new \Ds\String\Unicode(

"Jag älskar regnbågar och sköldpaddor"

);

Alternatively, we can utilize the use keyword to import the class, so we do not have to qualify it fully:

use Ds\String\Unicode;

$string = new Unicode(

"Jag älskar regnbågar och sköldpaddor"

);

This works in the global scope, or within any other namespaced code—though the use statement must be declared after any namespace declaration.

You may have as many use statements as you like, and may use more than one class or interface at a time using a comma-separated list.

Note that use applies to the file in which it is declared, and when that file is included, does not have any effect in the including file.

You do not have to use the fully-qualified name; you can also use just a portion of it, allowing you to access sub-elements from that point:

use Ds\String;

$string = new String\Unicode(

"Jag älskar regnbågar och sköldpaddor"

);

Dynamic Usage

If you want to refer to a namespaced item dynamically, you must use the fully-qualified namespace.

$class = "\Ds\String\Unicode";

$string = new $class("Jag älskar regnbågar och sköldpaddor");

As of PHP 5.5, a magic class construct is available that will return the fully-qualified name of a class. The construct is accessed like a constant, but is not case-sensitive:

namespace Ds\String;

$class = Unicode::CLASS; // \Ds\String\Unicode

The “constant” name CLASS is case-insensitive, and can also be written Unicode::Class or Unicode::class.

Aliasing

It is also possible to alias items using the use keyword:

use Ds\String\Unicode as String;

$string = new String(

"Jag älskar regnbågar och sköldpaddor"

);

This is useful when working with multiple namespaces that may reuse the same name in different contexts.

Importing Functions and Constants

Since namespaces were introduced, you have been able create functions and constants within them. However, until PHP 5.6 you could not import those functions and constants, and had to refer to them by either their fully-qualified name or an aliased name.

For example, with the function below:

Listing 1.19: A namespaced function

namespace Unicode\Tools;

const CHARSET = 'UTF-8';

function strlen($string) {

if (extension_loaded('iconv')) {

return \iconv_strlen($string);

} elseif (extension_loaded('mbstring')) {

return \mb_strlen($string);

}

return false;

}

we would need to do one of the following in PHP 5.3 through 5.5:

Listing 1.20: Accessing a namespaced function before 5.6

use Unicode\Tools as UT;

$charset = UT\CHARSET;

UT\strlen("Jag älskar regnbågar och sköldpaddor");

// or, fully-qualified

$charset = \Unicode\Tools\CHARSET;

\Unicode\Tools\strlen(

"Jag älskar regnbågar och sköldpaddor"

);

With PHP 5.6 we can import functions and constants and even override built-in PHP functions/constants in the global space. You can still access the global space by using the fully-qualified syntax (\function() or \CONSTANT). To import functions we use the use function syntax; for constants, use const:

use const Unicode\Tools\CHARSET;

use function Unicode\Tools\strlen;

$charset = CHARSET; // Is now Unicode\Tools\CHARSET

strlen($string); // Is now Unicode\Tools\strlen(),

// not the global \strlen()

Unlike with classes and interfaces, when inside a namespace, unqualified namespaces and constants that do not exist relative to the current namespace will fall back to the global scope—even if you explicitly use function or use const with a non-existent function/constant.

Listing 1.21: Falling back to global scope

namespace Ds\String;

// Does't exist, or it would override

use function Ds\String\iconv_strlen;

// The same as using fully qualified \iconv_strlen()

echo iconv_strlen(

"Jag älskar regnbågar och sköldpaddor"

); // outputs 36

It is recommended (despite the fall-through) that you fully qualify global functions/constants; otherwise, adding in a function or constant of the same name later will cause the new item to be used instead.

Summary

This chapter has covered many of the essentials of any PHP application. While simple, they are the building blocks of all applications, so you should therefore be completely familiar with them, their capabilities and any special requirements that they have.

There are some fundamental elements that we have only glossed over here: arrays, strings, functions and objects. These are complex enough to warrant their own sections of the book and are, therefore, covered in the next four chapters.