Chapter 3. PHP and MySQL

Drupal is written purely in PHP, an open source, interpreted scripting language. PHP relies on multidimensional arrays for a lot of its processing, and Drupal continues this methodology. PHP has evolved into a serious object-oriented programming language that powers many of the hottest sites on the Internet today.

MySQL is an open source relational database engine that runs pretty efficiently on Linux platforms. Since its humble beginnings, MySQL has evolved into a scalable, robust database platform that's supported in many different operating environments.

This chapter will familiarize you with the basic syntax of PHP and introduce MySQL, with an emphasis on how Drupal uses both technologies, and is aimed at the programmer who already knows one or more computer languages. I will make references to C#, since most Windows programmers have had exposure to C# or some related language at some point in their career.

PHP

PHP is a server-side scripting language for generating HTML. Actually, PHP can also be used as a stand-alone scripting language, but for Drupal, PHP is used as a powerful scripting language to extract information from databases, apply business rules and theming instructions, and eventually create HTML that is sent to a browser.

No one really knows what PHP actually stands for any more. When it was first created, the initials stood for "personal home page." At some point, the name behind the letters was changed to reflect its use beyond web pages. By that time, the term "PHP" had a life of its own, so the developers kept the acronym and made up a recursive definition. So now, if you insist, PHP stands for "PHP: Hypertext Processor." But I guess it's probably safer to just say that PHP stands for PHP.

For the Windows developer familiar with Microsoft tools, it is probably best to think of PHP as an analogue to classic Active Server Pages (ASP). As with ASP, you can mix PHP code with HTML to create your output page.

I suppose you could also think of PHP as similar to ASP.NET except there's no concept of "code behind." That is, all PHP code is clear text, and is normally interpreted on the fly, though there are some pre-compilers that will shorten execution time.

PHP is pretty easy to learn, particularly if you have experience with any language that has a C-like syntax, such as Java or C#. In learning PHP, you'll notice two things pretty quickly:

  • PHP is very rich in built-in functions. You'll find a built-in function to do pretty much anything you want.

  • PHP in general—and Drupal in particular—make heavy use of arrays. Learn to love them.

I must say I thought I was all finished with interpreted languages, but I've come to enjoy coding in PHP. If you are a C# programmer like me, you may be frustrated at first, but give it a few days and you might end up finding the same comfort.

Just Enough PHP Syntax

This section is not intended for the new programmer. It is also not my intent to make you fluent in all aspects of PHP. It is a very rich language, and even I haven't been all of the way through it. Rather, this section is meant to get you up to speed pretty quickly, to acquaint you with some of the unique features of PHP and show how the syntax might differ from what you already know. The emphasis is on how PHP is used in Drupal.

To learn more about PHP syntax, I recommend downloading one of the syntax manuals from the PHP site, http://php.net/download-docs.php. I like the CHM version because it is self-contained and works well on Windows development machines. The web version of the manual at http://php.net/manual has the same material, but also includes a lot of user comments, recommendations, examples, and, of course, the predictable rantings. The online version is worth reading if you have a fast connection, if only for the occasional gold nugget you'll get in the comments sections.

Here are the basics of PHP.

Scripting Blocks

PHP scripting blocks are specified using starting and ending tags with the form:

<?php
?>

Much like ASP, these blocks can be placed anywhere in the file that is executing. Anything that is not inside of a PHP block will be send directly to the browser. This allows you to insert HTML directly, along with PHP scripting blocks that get something from the server:

<html>
    <body>
        <p>The current time is: <?php print date("g:i a"); ?></p>
    </body>
</html>

Tip

The PHP start block indicator, <?php, is required. The end marker, ?>, is necessary only when you are placing an inline code snippet. If you have a page that contains PHP code, you don't need to insert the end marker. In fact, the end marker is deprecated, particularly in Drupal, because an accidental space after the end marker can cause unpredictable results.

Comments

Inside of a scripting block, comments follow the C/Java conventions:

<?php
/*
Block comments use this form.
*/

// everything to the right of double slashes on a line is considered a comment

In Drupal, like in .NET, there are some conventions for generating documentation for your code. Visual Studio's IntelliSense processor makes use of some of these, so you should use them as well.

Drupal uses the Doxygen documentation framework. We'll get to that in a bit.

Types

PHP is a loosely typed language. Variables need not be declared before they are used; a variable that is assigned a value is declared then and there. So,

$bee_count = 10000;

is the simplest form of a variable declaration/assignment.

Table 3-1 shows the types available in PHP.

Table 3.1. PHP Types

Type

Description

Values

Notes

Boolean

Indicates a truth value.

TRUE or FALSE

0 is considered FALSE, while any non-zero number (including −1) is TRUE.

Integer

Signed whole number.

32- or 64-bit depending on your OS

PHP will convert overflowed integers to float.

Float

Decimal number.

Depends on platform, but typically ~1.8e308

Can be represented in several different ways, such as 1.234, 1.2e3, 7E-10.

String

A series of characters typically consisting of bytes.

The size of a string is limited only by the available memory.

Double-quoted and single-quoted strings act differently, as I'll discuss later.

Array

A loosely typed collection of related data.

The size of an array is limited only by the available memory.

Array members can be accessed by index or key. You'll find more information about arrays below.

Object

A fundamental part of PHP's object-oriented programming functionality.

The characteristics of an object are defined by the object itself.

With PHP 5, objects were added to make PHP more competitive with other OOPs like Java and C++.

Resource

A special variable that holds a reference to some external resource.

Can be virtually any resource, such as a database, a Java program, an LDAP table, or an XML document.

Once freed, a resource is removed by the garbage collector.

NULL

A special variable that represents no value.

The only value this object can contain is NULL.

Casting a variable to NULL will remove the variable and unset its value. It is important to note that NULL is different from FALSE, which is a value.

Type conversion is done automatically by the PHP engine when a conversion is required. For example, a number can usually be accessed as a string without a lot of trouble. And a string can be treated as a number if it looks like a number.

There are some simple rules for variable names:

  • A variable name must start with a letter or an underscore "_".

  • A variable name can only contain alphanumeric characters and underscores (a-z, A-Z, 0-9, and _ ).

  • A variable name can't contain spaces. If a variable name is more than one word, Drupal conventions dictate that it should be separated with an underscore ($my_string).

The scope of a variable is generally within the context in which it is defined. So it is possible that the same variable name will point to two different variables.

The global keyword provides a way to access globally defined variables that would otherwise not be visible. For example:

<?php
$g = 42;

function return_g() {
  // undefined variable $g
  return $g;
}
?>
<p>The value of $g is: <?php print return_g();?></p>

The function return_g() will not produce any output because $g is not defined within the context of the function. If we define $g with the global keyword, however, it does pull the variable into the function:

<?php
$g = 42;

function return_g() {
  global $g;
  return $g;
}
?>
<p>The value of $g is: <?php print return_g();?></p>

The second way to access global variables is by using the $GLOBALS array.

<?php
$g = 42;

function return_g() {
  return $GLOBALS['g'];
}
?>
<p>The value of $g is: <?php print return_g();?></p>

The reason the $GLOBALS variable is significant is that it lets you access any of the myriad global variables that are available at a given time. Drupal makes extensive use of this, for example, to access global variables having to do with the state of the current page-request conditions:

return $GLOBALS['base_url'] .'/'.
        file_directory_path() .'/'.
        str_replace('', '/', $path);
$variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
$account = $GLOBALS['user'];

And then there are the "superglobal" variables, which mostly have to do with the state of the parser or web server. Superglobals don't need the global keyword:

$base_url = $base_root .= '://'. $_SERVER['HTTP_HOST'];
$query = drupal_query_string_encode($_GET, array('q'));
$form_state = $_SESSION['batch_form_state'];

The superglobals are:

  • $GLOBALS: An associative array containing references to all variables that are currently defined in the global scope of the script. The variable names are the keys of the array.

  • $_SERVER: An array containing information such as headers, paths, and script locations. The entries in this array are created by the web server. There is no guarantee that every web server will provide any of these; servers may omit some, or provide others not listed here.

  • $_GET: An associative array of variables passed to the current script via the URL parameters.

  • $_POST: An associative array of variables passed to the current script via the HTTP POST method.

  • $_FILES: An associative array of items uploaded to the current script via the HTTP POST method.

  • $_REQUEST: An associative array that by default contains the contents of $_GET, $_POST, and $_COOKIE.

  • $_SESSION: An associative array containing session variables available to the current script.

  • $_ENV: An associative array of variables passed to the current script via the environment method. These variables are imported into PHP's global namespace from the environment in which the PHP parser is running. Many are provided by the shell in which PHP is running, and as different systems are likely running different kinds of shells, a definitive list is impossible.

  • $_COOKIE: An associative array of variables passed to the current script via HTTP cookies.

  • $php_errormsg: A variable containing the text of the most recent error message generated by PHP. This variable is available only within the scope in which the error occurred, and only if the track_errors configuration option is turned on (it defaults to off).

  • $HTTP_RAW_POST_DATA: Raw POST data.

  • $http_response_header: HTTP response headers.

  • $argc: The number of arguments passed to a script. Contains the number of arguments passed to the current script when run from the command line. Note that the script's filename is always passed as an argument to the script, so the minimum value of $argc is 1. Since Drupal is a web-based system, this variable is not used.

  • $argv: An array of arguments passed to script. Contains an array of all the arguments passed to the script when run from the command line. Note that the first argument is always the current script's filename, so $argv[0] is the script's name. Since Drupal is a web-based system, this variable is not used.

Note

A Drupal global variable can often provide the information you're looking for. It is always better to use the Drupal-managed global variable than the PHP-supplied variable. For example, using the Drupal-managed variable allows other modules to modify it before you get it, which may keep you from doing a lot of work. Plus, it's the polite thing to do. Drupal's global variables can be found in global.inc, and are documented at http://api.drupal.org/api/drupal/developer--globals.php/7, and explained in Appendix C.

Strings

Strings in PHP are straightforward. Like any other variable, a string can be declared and assigned at the same time:

$winning_name = 'Spaceship One';

PHP has a string concatenation operator, which is a period:

$full_name = $first_name . ' ' . $last_name;

You can use single quotes or double quotes to delimit strings. If you want to use a literal quote inside of your string, it must be escaped with a backslash:

$warning = 'If you kids don't stop yelling, I'm coming back there!';

By single-quoting a string ('), everything in the string (except escaped characters) is a literal. But if you use double quotes ("), the PHP engine will parse the string looking for variables:

$address = '123 Elm St';
print "I live at $address.";

You can also put the variable in curly braces if the string requires close concatenation:

$insect_type = 'bee';
$insect_qty = 25;
print "I caught $insect_qty {$insect_type}s yesterday!";

If you use the curly-brace method, your string must be delimited in double quotes. Another form of string variable in PHP is called "heredoc." It is usually used for multiple-line string values. I use it a lot when creating complex SQL query strings. A heredoc-formatted string is in the form:

$str = <<<EOS
Haikus are easy.
But sometimes they don't make sense.
Refrigerator.
EOS;

The text following the three angle brackets is the delimiter. As soon as the PHP string parser sees the same text, the string ends. Heredoc strings act like double-quoted strings, and so variable substitution is done:

$which_name = 'Reginald';
$query = <<<Query
SELECT title
    FROM report
    WHERE name = '$which_name'
    ORDER BY title
Query;

Drupal does not make very extensive use of heredoc-formatted strings, but they are available if you need them. The SQL query example shown here is an acceptable way to create queries in Drupal, but is becoming more and more deprecated as the Drupal core and contributed modules move towards the data abstraction layer. I cover this extensively in Chapter 9.

PHP has a wide range of string processing functions, such as strlen(), and strpos(), as well as functions for doing Perl-style regular expression processing.

Tip

PHP is not Unicode-aware. There are, however, plenty of Unicode-friendly string-processing functions in Drupal. They are defined in unicode.inc and documented at http://api.drupal.org/api/drupal/includes--unicode.inc/7.

Operators

PHP has a pretty standard set of operators, which are similar to those in C or Java. One thing you might find new, however, is the concatenation assignment operator. It works just like any other assignment operator, however, so the following are equivalent:

$string = $string . ', and that's what she said';
$string .= ', and that's what she said';

PHP provides bitwise operators that can also assign. Many of these are used in the Drupal core (see Table 3-2).

Table 3.2. Bitwise Operators

Assignment Operator

Example

Bitwise And

$a &= $b

Bitwise Or

$a |= $b

Bitwise Xor

$a ^= $b

Left shift

$a <<= $b

Right shift

$a >>= $b

In addition to comparison operators (==, !=, etc.), PHP also has an "identical" operator that indicates that two objects have the same type. Therefore, $a === $b is true if $a and $b are equal, and of the same object type. This is used in Drupal to test if something is really TRUE or FALSE, instead of the integers 1 or 0. For example, the destination member of the file object contains either the path to the file or a FALSE, indicating that it was not set. The test, if ($file->destination == FALSE), would pass if the destination was set and its value was a literal string '0'. Instead, use the type test to differentiate between '0' and FALSE: if ($file->destination === FALSE).

Conditional Statements

PHP has the expected set of conditional statements, namely if and switch. The syntax is similar to many other languages:

if (knock_at_door()) {
  answer_door();
}
else {
  sit_on_couch();
}

Drupal conventions suggest that braces always be used, even if they are not necessary. In the example above, braces are not required because there is only one statement in each block. But braces make the code more robust because someone might add statements without remembering to also add braces.

The switch statement is also similar to those in other languages:

switch ($number) {
  case 0:
    print 'You typed zero.';
    break;
  case 1:
  case 9:
    print 'number is a perfect square.';
    break;
  case 2:
    print 'number is an even number.';
  case 3:
  case 5:
  case 7:
    print 'number is a prime number.';
    break;
  case 4:
    print 'number is a perfect square.';
  case 6:
  case 8:
    print 'number is an even number.';
    break;
  default:
    print 'Only single-digit numbers are allowed.';
    break;
}

The label following the case statement must be a static value, not a variable or a function. However, you can do some evaluation based on the variable in the switch statement. Consider the following:

switch ($month) {
  case 'Jan':
  case 'Feb':
  case 'Mar':
    $quarter = "Q1";
    break;
  case 'Apr':
  case 'May':
  case 'Jun':
    $quarter = "Q2";
    break;
  case 'Jul':
  case 'Aug':
  case 'Sep':
    $quarter = "Q3";
    break;
  case 'Oct':
  case 'Nov':
  case 'Dec':
    $quarter = "Q4";
    break;
}

Changing the way the cases are evaluated actually runs more efficiently:

switch ($month) {
  case ($month=='Jan' || $month=='Feb' || $month=='Mar'):
    $quarter = "Q1";
    break;
  case ($month=='Apr' || $month=='May' || $month=='Jun'):
    $quarter = "Q2";
break;
  case ($month=='Jul' || $month=='Aug' || $month=='Sep'):
    $quarter = "Q3";
    break;
  case ($month=='Oct' || $month=='Nov' || $month=='Dec'):
    $quarter = "Q4";
     break;
}

This feature allows you do to things that aren't as enumerated:

switch ($hour) {
  case ($hour < 12):
    print: 'am';
    break;
  case ($hour < 24):
    print: 'pm';
    break;
  default:
    print: 'fail';
    break;
}

Note

If you are accustomed to the way C# handles case statement fall-through, you should be aware that PHP handles this condition the same way C and C++ do, which is different from the way C# handles things.

In C#, the code will only fall through from one case to the next when there is no code between the cases. If you have code in a case, you need either a break or a goto or the compiler will issue an error.

In PHP, if you don't put a break in each case, you'll fall through to the next case statement and your compiler will not complain. While I prefer this old-style method (I never got used to the C# way of doing things), you could set yourself up for some unintended consequences.

Objects

Version 5 of PHP brought a rewritten and pretty nearly full implementation of an object model. PHP objects should be familiar to anyone who has worked in other object-based languages, like Java or C#.

Classes are declared using the class keyword. Inside, methods and properties are declared as functions and variables, as shown in Listing 3-1.

Example 3.1. PHP Classes Are Declared Using the class Keyword

<?php

class NewClass
{
  // property declaration
  public $var = 'some default value';

  // method declaration
  public function showProperty() {
    return $this->var;
  }
}

Properties are accessed using the -> accessor. This is analogous to the "dot" accessor used by C#.

$myClass = new NewClass();
print 'property: ' . $myClass->var;
$myClass->var = 'changed value';
print '<br>method: ' . $myClass->showProperty();

This will print:

property: some default value
method: changed value

PHP classes also have the usual characteristics of modern object-oriented languages:

  • Public, private, and protected visibility of methods and properties

  • Static methods and properties

  • Class constants

  • Constructors and destructors

  • Inheritance

  • Class abstraction

  • Object interfaces

And there are other features that make PHP objects pretty powerful. But PHP also has something odd—what it calls "overloading" is not what overloading means in most other object-oriented languages.

There's nothing to keep you, the module developer, from using classes in this way. PHP classes provide a handy, powerful way to deal with the logic of your application. You should know, though, that using classes in this way might make your module less than maintainable by members of the Drupal community.

The Drupal core, and many contributed modules, use classes, but not in this formal, declarative way. Rather, classes are created on the fly using the stdClass object.

Take, for example, the $user variable. This object contains information about the currently logged-in user. If there's no one logged in, then it contains default, anonymous, properties. See Listing 3-2.

Example 3.2. Classes Are Created on the Fly and Properties Set and Returned to the Caller

function drupal_anonymous_user($session = '') {
  $user = new stdClass();
  $user->uid = 0;
  $user->hostname = ip_address();
  $user->roles = array();
  $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
  $user->session = $session;
  $user->cache = 0;
  return $user;
}

You can then access these properties from your module:

$user = drupal_anonymous_user();
print $user->hostname;

Drupal 7 is the first version that uses classes extensively in the core. The main driver here is the new Simpletest module. Prior to version 7 of Drupal, Simpletest was a contributed module. With the desire to be more test-centered, the head Drupal developers decided to include Simpletest in the core. I talk about test-driven development in Chapter 8.

To create tests, you must create a class that extends the DrupalWebTestCase class. Since tests are required for core modules and expected for contributed modules, I expect to see more use of classes in contributed modules as more are developed for Drupal 7.

Arrays

As I mentioned earlier, there is a heavy emphasis on arrays in Drupal. You will either learn to love arrays, or you will learn to hate Drupal. Arrays are very powerful, so once you understand how they are used, you will probably learn to love them.

PHP has three different types of arrays:

  • Numeric: An array with a numeric index.

  • Associative: An array where each ID key is associated with a value. This is sort of like a Hashtable or Dictionary object in .NET.

  • Multidimensional: An array containing one or more arrays.

You can declare a numeric array by simply listing the values in an assignment:

$beers = array ('ale', 'lager', 'stout', 'wheat'),

Or you can assign the values to specific indexes:

$beers[0] = 'ale';
$beers[1] = 'lager';
$beers[2] = 'stout';
$beers[] = 'wheat';

In this example, both types of assignments result in the same array. Notice that the fourth line omits the number in the brackets. This results in a new item being added to the array with the index being the next available integer, which is 3. In fact, this example could have omitted the numbers in all four lines.

An associative array is an array in which each item has a key associated with the value:

$beer_color['ale'] = 'pale';
$beer_color['lager'] = 'extra pale';
$beer_color['stout'] = 'dark';
$beer_color['wheat'] = 'pale';

Each key in a given array must be unique, but the values need not be. If you attempt to add an array member with a key that already exists, you will not generate an error. Rather, you will overwrite the value that is already set in that member.

You can use the key association operator, =>, as a syntactical shortcut to create array members and assign them a value at the same time. In the previous example, I could have written the assignments like this:

$beer_color = array(
  'ale' => 'pale',
  'lager' => 'extra pale',
  'stout' => 'dark',
  'wheat' => 'pale',
);

Each of the examples I've shown so far contain a one-dimensional array. If the value of an array is another array, you've got yourself a multidimensional array. The => key association operator is used, in this case, to create sub-arrays and assign values to them:

$beers = array (
  'ale' => array (
    'English Bitter',
    'Pale Ale',
    'Scottish Ale',
  ),
  'lager' => array (
    'American Lager',
    'Pilsner',
    'Marzen',
  ),
  'stout' => array (
    'Sweet Stout',
    'Cream Stout',
  ),
    'wheat' => array (
      'Weizen' => array (
        'Dunkelweizen',
        'Weizenbock',
        ),
      'Berliner Weisse',
      'Wit',
      )
);

Notice that multidimensional arrays can be symmetrical or asymmetrical. The values in this array can be accessed by walking down the array tree:

print "I believe I'll have a {$beers['ale'][1]}";

Prints out, "I believe I'll have a Pale Ale".

Notice that the 'wheat' item has an array that includes 'Weizen'. Notice, also, that 'Weizen' itself is an array. So $beers['wheat']['Weizen'][0] resolves to 'Dunkelweizen'

Note

If an array member is itself an array, you must specify the array accessor of the internal array in order for it to evaluate. For example, printing $beers['wheat']['Weizen'] in the previous example will result in a reference to an object, not the value of the first array member.

PHP has dozens of functions that act on arrays, making it convenient and easy to use this type of variable structure. You'll find functions for sorting, printing, copying, searching, splitting, combining, and for doing other helpful tasks.

If you want to see the structure of an array, use the recursive-print function, print_r. So, print_r ($beers); yields:

Array
(
    [ale] => Array
        (
            [0] => English Bitter
            [1] => Pale Ale
            [2] => Scottish Ale
        )

    [lager] => Array
        (
            [0] => American Lager
[1] => Pilsner
            [2] => Marzen
        )

    [stout] => Array
        (
            [0] => Sweet Stout
            [1] => Cream Stout
        )

    [wheat] => Array
        (
            [Weizen] => Array
                (
                    [0] => Dunkelweizen
                    [1] => Weizenbock
                )

            [0] => Berliner Weisse
            [1] => Wit
        )
)

In Drupal, you'll often see arrays of objects. To access an object in PHP, you use the -> accessor:

if ($files[$filename]->status == 0)
$result = isset($variables['node']->tid) ? $variables['node']->tid : 0;

It gets more fun when objects inside the array are arrays themselves:

$regions = $theme_data[$theme]->info['regions'];

This example looks into the $theme_data array and finds the key that matches $theme. It then takes the result and gets the info property (which is an array). Finally, the value of the array member that has a key of 'regions' is returned.

As you can see, PHP arrays provide a lot of flexibility, but you need to be careful that you are using the indexes correctly.

Loops

PHP has four looping constructs:

  • while: loops through a block of code while a specified condition is true.

  • do...while: loops through a block of code once, and then repeats the loop as long as a specified condition is true.

  • for: loops through a block of code a specified number of times.

  • foreach: loops through a block of code for each element in an array.

The while and do...while loops iterate through a block of code waiting for a condition to become true. The while loop will first evaluate the condition and then execute the block if it is true:

$temperature = 65;
while ($temperature < 71) {
  $temperature = adjust_thermostat(1);
}

The do...while loop is guaranteed to execute the block at least once because the condition is evaluated after the block has been executed.

$i=1;
do {
  $i++;
  echo "The number is " . $i . "<br />";
}
while ($i <= 5);

The for loop will iterate through a block a certain number of times depending upon a condition, which is evaluated each time the structure loops.

for ($i = 1; $i < 100; $i += 2) {
  print $i;
}

This code will print every odd number between 0 and 100.

The foreach loop is helpful in that it efficiently iterates through an array. This control structure is used extensively in Drupal.

The foreach loop uses the "as" token to assign the value of an item in the array to a new variable. In this case, $beer will contain a different value each time the loop executes.

foreach ($beers as $beer) {
  print ("{$beer[0]}
");
}

This will print

English Bitter
American Lager
Sweet Stout
Berliner Weisse

for the $beer array declared earlier in this chapter.

In C#, the values are reversed and the keyword "in" is used:

foreach (int beer in beers)

Drupal makes heavy use of a slightly different foreach structure when it comes to keyed arrays. The expanded use takes this form:

foreach ($beers as $beerkey => $beervalue) {
  print ("key: {$beerkey}, value: {$beervalue[0]}<br>");
}

This will print out both the key and value:

key: ale, value: English Bitter
key: lager, value: American Lager
key: stout, value: Sweet Stout
key: wheat, value: Berliner Weisse

Functions

A function provides a way of executing common code from other places. This example shows the basic syntax for a function:

function get_something () {
  $query = 'SELECT something FROM {somewhere}';
  $result = db_query($query);
  return $result;
}

A function is called by name:

$record_set = get_something();

Like variables, functions are loosely typed. The type of the return value is interpreted when the function is called.

As you might expect, functions can have parameters—or not. PHP functions can even have a variable number of parameters. Parameters are declared in the function declaration:

function get_something_else ($search_term) {
  $query = "SELECT something FROM {somewhere} WHERE $search_term";
  $result = db_query($query);
  return $result;
}

This function is called by name, passing parameters:

$record_set = get_something_else("this = 'that'");

To set a default value for an argument, you can assign it in the function declaration:

function get_beer($quantity = 6) {
  print "I'll get $quantity beers.";
}

You can pass arguments by value, as we have so far, or you can pass by reference, which sends a pointer to the value. This is handy if you want to affect the values of many variables, or if you have a large value and you don't want to pass the whole thing to the function.

function twss(&$string) {
  $string .= ' That's what she said.';
}
$str = 'There are a lot of crabs on that rock.';
twss($str);
print $str; // now contains the extra sentence.

A PHP function can be called with a variable number of arguments. This is used a lot in Drupal and works using the function argument functions:

function food_list() {
  $numargs = func_num_args();
  print "Number of foods: $numargs<br>
";
  if ($numargs >= 2) {
    print 'The second food is: ' . func_get_arg(1) . "<br>
";
  }
  $arg_list = func_get_args();
  for ($i = 0; $i < $numargs; $i++) {
    print "Food $i is: {$arg_list[$i]}<br>
";
  }
}

food_list('tomato', 'egg', 'rutabaga', 'pie'),

Exceptions

PHP has a try...catch block for handling exceptions. Earlier versions of Drupal don't make much use of try...catch, but the maintainers of the core are using it more and more in version 7, and so are the authors of contributed modules. The syntax is the same as in Java or C#:

function inverse($x) {
  if (!$x) {
      throw new Exception('Division by zero.', 4321);
  }
  else return 1/$x;
}

try {
  print inverse(5) . "
 ";
  print inverse(0) . "
 ";
} catch (Exception $e) {
  print "Caught exception ({$e->getCode()}): {$e->getMessage()}
";
}

// Continue execution
print 'Hello World
';

This will output:

0.2
Caught exception (4321): Division by zero.
Hello World

One thing to note is that PHP does not have a finally block like C# does. As with C#, however, you can create your own exception classes in PHP.

This should get you started with PHP syntax. PHP is a very rich language with plenty of handy features. There's a relatively new object model with PHP 5. Drupal doesn't make very much use of this, but you certainly can in your own modules.

I encourage you to go to the PHP site and check out the language reference if you want to learn more about the details of PHP syntax. Or, you can just dive in to your Drupal site and start to figure things out.

Drupal Coding Conventions

The Drupal developer community provides a set of style conventions for PHP programmers, which is intended to make your code maintainable by others should you want to give it to the community.

The Drupal Coding Standards cover everything from the use of white space and indenting to a suggested use of database calls and source control specifics.

Before starting your programming project, it's a good idea review those standards and develop good habits early. At first, I had trouble with the standards concerning braces. It's different from how Visual Studio handles C# braces by default. Also, the Drupal standards insist on indenting with 2 spaces at a time instead of tabs. I found that by adopting these new standards early, they became second nature pretty quickly.

To get you started, here are the basics:

  • Use an indent of two spaces, with no tabs.

  • Lines should have no trailing whitespace at the end.

  • Files should be formatted with as the line ending (the Unix line ending), not (the Windows line ending).

  • All text files should end in a single newline ( ).

  • All binary operators (+, –, =, !=, ==, etc.) should have a space before and after the operator, for readability. For example, an assignment should be formatted as $foo = $bar; rather than $foo = $bar;. Unary operators, such as ++, should not have a space between the operator and the variable or number they are operating on.

  • Put a space between the (type) and the $variable in a cast: (int) $mynumber.

This keeps the code tight and readable.

I had a bit of trouble with this formatting style, as I was accustomed to Visual Studio's C# defaults. Fortunately, you can customize Visual Studio to deal with the different guidelines. Plus, there are PHP code beautifiers with Drupal's conventions built-in if you insist on sticking with your style.

Control Structures

Control structures use the old Pascal-style braces and indenting.

Control statements should have one space between the control keyword and opening parenthesis, to distinguish them from function calls.

if (my_hair == 'on fire') {
  douse_with(water);
}
elseif (is_cold && !windy()) {
  zip_jacket();
}
else {
  print 'keep walking';
}

You should always use curly braces, even in situations where they are technically optional.

Function Calls

Here are some simple rules for function calls:

  • Functions should be called with no spaces between the function name, the opening parenthesis, and the first parameter.

  • Put spaces between commas and each parameter.

  • No space between the last parameter, the closing parenthesis, and the semicolon, like so:

    $var = foo($bar, $baz, $quux);

Use one space on either side of an equals sign used to assign the return value of a function to a variable.

In the case of a block of related assignments, more spaces may be inserted to promote readability:

$short         = foo($bar);
$long_variable = foo($baz);

Function Declarations

Functions should be declared using the following tight form:

function funstuff_system($field) {
  $system['description'] = t('This module does random things.'),
  return $system[$field];
}

Arguments with default values usually go at the end of the argument list.

Always attempt to return a meaningful value from a function if one is appropriate.

Arrays

Arrays should be formatted with a space separating each element (after the comma), and spaces around the => key association operator, if applicable:

$some_array = array('hello', 'world', 'foo' => 'bar'),

Note that if the line declaring an array spans longer than 80 characters (often the case with form and menu declarations), each element should be broken into its own line, and indented one level:

$form['title'] = array(
  '#type' => 'textfield',
  '#title' => t('Title'),
  '#size' => 60,
  '#maxlength' => 128,
  '#description' => t('The title of your node.'),
);

Note the comma at the end of the last array element. Fortunately, PHP does not flag this as an error like some programming languages do. While it is optional, it helps prevent parsing errors if another element is later placed at the end of the list.

Naming Conventions

  • Classes and interfaces should use UpperCamel naming.

  • Methods and class properties should use lowerCamel naming.

  • Classes should not use underscores in class names unless absolutely necessary to derive inherited class names dynamically.

  • Interfaces should always have the suffix "Interface".

  • Protected or private properties and methods should not use an underscore prefix.

In addition to these style conventions, there are a number of conventions regarding the use of certain preferred PHP constructs and higher-level calls, such as database access and communication with other Drupal hooks.

  • Use try...catch exception handling wherever possible.

  • Put a CVS header at the top of each code file.

  • Don't use reserved words for database column or table names

  • Avoid "SELECT * FROM ...". In fact, avoid embedding SQL in your code at all. Instead, use the data abstraction layer. See Chapter 9.

  • Use Unicode functions for strings so your code is translatable.

For more information, see the Drupal Coding Standards at http://drupal.org/coding-standards.

Doxygen

If you are a .NET programmer, you are probably familiar with Visual Studio's XML Documentation facility. With the careful placement of properly formatted comments, you can create a base document that you can later expand to document your system.

In the Drupal world, this facility is called Doxygen. In order for Doxygen to read comments as documentation directives, you need to observe the careful placement of properly formatted comments.

To document a block of code, the syntax is:

/**
 * Documentation here.
 */

Doxygen will parse any comments located in such a block. You should use as few Doxygen-specific commands as possible, to keep the source legible.

Any mentions of functions or file names within the documentation will automatically link to the referenced code, so typically no markup need be introduced to produce links.

To document a block of code, you should use the block comments, but add an extra asterisk on the first line as shown in Listing 3-3.

Example 3.3. Block Comments Can Be Used to Generate Documentation Using Doxygen

/**
 * Summary here; one sentence on one line should not exceed 80 chars).
 *
 * A more detailed description goes here.
 *
 * A blank line forms a paragraph. There should be no trailing white-space
 * anywhere.
 *
 * @param $first
 *   "@param" is a Doxygen directive to describe a function parameter. Like some
 *   other directives, it takes a term/summary on the same line and a
 *   description (this text) indented by 2 spaces on the next line. All
 *   descriptive text should wrap at 80 chars, without going over.
 *   Newlines are NOT supported within directives; if a newline would be before
 *   this text, it would be appended to the general description above.
 * @param $second
 *   There should be no newline between multiple directives (of the same type).
 * @param $third
 *   (optional) Boolean whether to do Third. Defaults to FALSE.
 *   Only optional parameters are explicitly stated as such. The description
 *   should clarify the default value if omitted.
 *
 * @return
 *   "@return" is a different Doxygen directive to describe the return value of
 *   a function, if there is any.
 */
function mymodule_foo($first, $second, $third = FALSE) {
}

There are many other conventions for adding documentation to your code using comments. See http://drupal.org/node/1354 for more information.

All documentation and comments always form proper sentences and use proper grammar and punctuation.

The full documentation for Doxygen is in the Drupal Coding Standards area.

MySQL

Drupal has a legacy of using MySQL as its database. In Drupal 7, one of the most important innovations is the new data abstraction layer. This more loosely coupled database layer allows for the use of other database engines. I'd prefer to use Microsoft SQL Server but, for now, MySQL is the norm. In Appendix A, I show you how to create a Drupal development environment based on IIS and SQL Server.

MySQL has the ability to store and execute stored procedures, but Drupal, by and large, does not make use of this capability. There's nothing stopping you, as a developer, from using stored procs but, since one of the goals of Drupal development is code sharing, using stored procs will make your code less maintainable by others simply because there is not an ethos of stored procedure use in the Drupal development community.

MySQL vs. SQL Server

If you are a Windows programmer, you probably have experience with Microsoft's SQL Server. If so, you are probably familiar with SQL Server's support for enterprise-level data storage, access, security, and integration. And you've probably heard of this little high school science project called MySQL. It even has a childlike name.

I hope you're sensing my sarcasm, because that's what I originally thought about MySQL:a science fair project created by a bunch of kids that accidentally made it into the mainstream. If so, you're like me, because that's what I thought when I first heard of MySQL. I was confident that Microsoft SQL Server was the best database out there, bar none. It has a long history of enterprise-level applications and, with the exception of that little "Slammer Worm" (http://en.wikipedia.org/wiki/SQL_Slammer) a few years ago that brought down the Internet, it's pretty secure. Well, I guess that was kind or a big problem, but Microsoft says they've fixed it. Actually, I was on the Microsoft campus during the months they were reeling from that bug, and I must say, they did a great job of stopping everything and going over every line of code in SQL Server to tighten things up. And they established guidelines for all programmers to prevent such things from happening in other Microsoft products in the future. Some of those guidelines have now been adopted by many projects, including Drupal.

Back to MySQL.

The more I work with MySQL, particularly the current version 5, the more impressed I am with its features and performance. It is certainly more than enough database for a content management system such as Drupal.

Before version 5, MySQL was criticized for its lack of stored procedures, triggers, and views. None of that mattered in a Drupal environment, though, since Drupal doesn't use those things. Stored procedures, triggers, views, cursors, and a few more things were added in version 5, but SQL Server still has the edge as it's had those features for many versions and they are rock-solid.

SQL Server definitely has the edge in enterprise features such as replication and advanced security; MySQL provides one-way replication and is pretty robust because it persists change data in a binary table. This allows replication even if the server goes down, as long as slave machines have been set up.

SQL Server uses clustering to provide high availability and it has a publish-subscribe replication model to provide two- or multi-way replication. SQL Server also provides a more robust recovery model because of its checkpoint logic. MySQL databases, when using the default MyISAM data structure, are prone to corruption in the event of a sudden power outage. MySQL supports alternative table structures for performance and stability reasons. INNODB is a popular structure for this.

Security is another area where Microsoft has quite an edge. MySQL provides basic security at the table level, but SQL Server has full column-level security. SQL Server has also passed C2 security certification from the NSA (No Such Agency), which means it can be used to spy on people. MySQL hasn't yet been certified and probably won't be until it gets a few more features, like column-level security.

But that's not really a problem in the context of a Drupal installation. Most of the database access in Drupal has to do with with managing content and configuration information on the site, not with enterprise-level secrets.

As for performance related to content management, SQL Server running on a Windows server and MySQL running on a Unix server are probably equivalent. MySQL may have a slight edge, just because it's quite a bit smaller and doesn't have to worry about all of the enterprise-level features I mentioned. When it comes to terabyte-sized databases, SQL Server wins hands down. But that's not usually what we have in a content management environment.

So SQL Server is a better database and we should still look down our noses at MySQL, right? Not really. I'm not ready to rewrite my enterprise applications to use MySQL, but for Drupal, I see little reason not to use MySQL unless you need to run in a SQL Server environment.

Oh, and I saved the best part for last. MySQL is free.

Enterprise Data

If you are a Windows programmer, you've probably worked on systems that access your enterprise data store. There's a good chance that the data is stored in some version of SQL Server. And since it's in an enterprise environment, you probably take advantage of clustering, mirroring, job scheduling, and perhaps analysis and integration services.

It's tempting, then, to think of using your enterprise SQL Servers as the data store for your Drupal system, especially if you're running your Drupal site on your Windows servers.

This might not be a good idea. Your enterprise data is probably sitting behind a DMZ where it is relatively safe from the outside world, while your web servers are probably in a less-secure environment and have less-sensitive information stored in them. Getting data from one side of the DMZ to the other requires some sort of service to communicate across the walls.

Since you will need a separate database for your customer-facing web sites anyway, MySQL makes sense because of its price and efficiency.

But if you want to use SQL Server for your Drupal installations, I'll show you how you can get started in Appendix A.

Summary

PHP and MySQL are critical technologies for understanding Drupal. PHP should feel pretty familiar to anyone who has used most any modern programming language as the syntax is identical or similar in most areas.

MySQL is a surprisingly powerful database, especially considering its genesis and open source status.

Later, in Chapter 9, I will discuss the database layer, which provides an API for accessing data in your deployed system, regardless of the database engine used.

First, though, in Chapter 4, we will go over the specifics of installing and configuring Drupal on your local machine.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.213.238