Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 85 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
85
Dung lượng
852,57 KB
Nội dung
388 Part III: Using PHP in Practice Creating the DataObject Class File Now comes the first of the classes that are used in the application. DataObject is an abstract class from which you can derive classes to handle database access and data retrieval. Because it ’ s an abstract class, you can ’ t instantiate (create objects from) it directly. In a moment, you create classes to handle both members and access log records that are based on the DataObject class. In OOP parlance, these types of classes are said to follow the active record design pattern, which means that the object contains the data for the record to store in or retrieve from the database, as well as the methods to carry out the actual storage and retrieval. Save the following script as DataObject.class.php in the book_club folder: < ?php require_once “config.php”; abstract class DataObject { protected $data = array(); public function __construct( $data ) { foreach ( $data as $key = > $value ) { if ( array_key_exists( $key, $this- > data ) ) $this- > data[$key] = $value; } } public function getValue( $field ) { if ( array_key_exists( $field, $this- > data ) ) { return $this- > data[$field]; } else { die( “Field not found” ); } } public function getValueEncoded( $field ) { return htmlspecialchars( $this- > getValue( $field ) ); } protected function connect() { try { $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $conn- > setAttribute( PDO::ATTR_PERSISTENT, true ); $conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); } catch ( PDOException $e ) { die( “Connection failed: “ . $e- > getMessage() ); } return $conn; } protected function disconnect( $conn ) { c13.indd 388c13.indd 388 9/21/09 9:12:02 AM9/21/09 9:12:02 AM Chapter 13: Retrieving Data from MySQL with PHP 389 $conn = “”; } } ? > So how does this class work? First of all, the script includes the config.php file so that it can access the database configuration constants: require_once “config.php”; The PHP require_once() function imports another PHP script into the current script in a similar way to require() , which you ’ ve used in previous chapters. The difference is that require_once() ensures that the file is imported only once. This is important if you create a large application with lots of script files, many of which need to include the same file, such as config.php . If you used require() , the PHP engine would include config.php every time it encountered the require() function call, resulting in multiple copies of the config.php file being included in the application (with, needless to say, chaotic results). Find out more about require_once() and related functions in Chapter 20. Next, the class declares a protected $data array to hold the record ’ s data. The fact that it ’ s protected means that the classes that derive from this class will be able to use it, but it ’ s still hidden from the outside world (as most properties should be). The first method, __construct() , is the class ’ s constructor. It ’ s called whenever a new object is created based on a class that ’ s derived from this class. The constructor accepts an associative array of field names and values ( $data ) and stores them in the protected $data array (assuming each field name exists in $data ). In this way it ’ s possible for outside code to create fully populated data objects. The getValue() method accepts a field name, then looks up that name in the object ’ s $data array. If found, it returns its value. If the field name wasn ’ t found, the method halts execution with an error message. getValue() enables outside code to access the data stored in the object. getValueEncoded() is a convenience method that allows outside code to retrieve a field value that has been passed through PHP ’ s htmlspecialchars() function. This function encodes markup characters such as < and > as & lt; and & gt; . Not only is this required when generating XHTML markup, but it ’ s also a good security measure that can help to reduce the risk of malicious markup making its way into your Web page. The final two protected functions allow classes to create a PDO connection to the database, as well as destroy a database connection. connect() creates a new PDO object and returns it to the calling code. Along the way, it sets a couple of useful attributes: $conn- > setAttribute( PDO::ATTR_PERSISTENT, true ); $conn- > setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); Setting the PDO::ATTR_PERSISTENT attribute to true allows PHP to keep the MySQL connection open for reuse by other parts of the application (or other applications). With this attribute set to false , which is the default setting, a new MySQL connection is opened every time a new PDO object is created in the application. Because setting up a new MySQL connection takes both time and resources, setting this attribute to true can help to improve performance. c13.indd 389c13.indd 389 9/21/09 9:12:03 AM9/21/09 9:12:03 AM 390 Part III: Using PHP in Practice Setting the PDO::ATTR_ERRMODE attribute to PDO::ERRMODE_EXCEPTION tells PDO to throw exceptions whenever a database error occurs, as you saw in the previous chapter. The disconnect() function merely takes a PDO object, stored in $conn , and assigns an empty string to $conn , thereby destroying the object and closing the connection to the MySQL database. Building the Member Class The Member class inherits from the DataObject class you just created. It ’ s responsible for retrieving records from the members table in the database. The class is relatively straightforward, because a lot of the work is delegated to the DataObject class. Save the following code as Member.class.php in your book_club folder: < ?php require_once “DataObject.class.php”; class Member extends DataObject { protected $data = array( “id” = > “”, “username” = > “”, “password” = > “”, “firstName” = > “”, “lastName” = > “”, “joinDate” = > “”, “gender” = > “”, “favoriteGenre” = > “”, “emailAddress” = > “”, “otherInterests” = > “” ); private $_genres = array( “crime” = > “Crime”, “horror” = > “Horror”, “thriller” = > “Thriller”, “romance” = > “Romance”, “sciFi” = > “Sci-Fi”, “adventure” = > “Adventure”, “nonFiction” = > “Non-Fiction” ); public static function getMembers( $startRow, $numRows, $order ) { $conn = parent::connect(); $sql = “SELECT SQL_CALC_FOUND_ROWS * FROM “ . TBL_MEMBERS . “ ORDER BY $order LIMIT :startRow, :numRows”; try { $st = $conn- > prepare( $sql ); $st- > bindValue( “:startRow”, $startRow, PDO::PARAM_INT ); $st- > bindValue( “:numRows”, $numRows, PDO::PARAM_INT ); $st- > execute(); c13.indd 390c13.indd 390 9/21/09 9:12:03 AM9/21/09 9:12:03 AM Chapter 13: Retrieving Data from MySQL with PHP 391 $members = array(); foreach ( $st- > fetchAll() as $row ) { $members[] = new Member( $row ); } $st = $conn- > query( “SELECT found_rows() AS totalRows” ); $row = $st- > fetch(); parent::disconnect( $conn ); return array( $members, $row[“totalRows”] ); } catch ( PDOException $e ) { parent::disconnect( $conn ); die( “Query failed: “ . $e- > getMessage() ); } } public static function getMember( $id ) { $conn = parent::connect(); $sql = “SELECT * FROM “ . TBL_MEMBERS . “ WHERE id = :id”; try { $st = $conn- > prepare( $sql ); $st- > bindValue( “:id”, $id, PDO::PARAM_INT ); $st- > execute(); $row = $st- > fetch(); parent::disconnect( $conn ); if ( $row ) return new Member( $row ); } catch ( PDOException $e ) { parent::disconnect( $conn ); die( “Query failed: “ . $e- > getMessage() ); } } public function getGenderString() { return ( $this- > data[“gender”] == “f” ) ? “Female” : “Male”; } public function getFavoriteGenreString() { return ( $this- > _genres[$this- > data[“favoriteGenre”]] ); } } ? > First the script includes the DataObject class file so that it can derive the Member class from DataObject . Next the class sets up the $data array keys, initializing each value to an empty string. Not only does this let you see at - a - glance the data that the Member class works with, but it also enables the DataObject class ’ s __construct() method to validate each field name that ’ s passed to it when creating the object. If a field name is passed that isn ’ t in the $data array, it ’ s rejected. The class also creates a private array, $_genres , to map the ENUM values of the favoriteGenre field in the members table (for example, “ nonFiction “ ) to human - readable strings (such as “ Non - Fiction ” ). c13.indd 391c13.indd 391 9/21/09 9:12:04 AM9/21/09 9:12:04 AM 392 Part III: Using PHP in Practice The next two static methods, getMembers() and getMember() , form the core of the class. getMembers() expects three arguments: $startRow , $numRows , and $order . It returns a list of $numRows records from the members table, ordered by $order and starting from $startRow . The records are returned as an array of Member objects. After calling the DataObject class ’ s connect() method to create a database connection, the method sets up the SQL statement to retrieve the rows: $sql = “SELECT SQL_CALC_FOUND_ROWS * FROM “ . TBL_MEMBERS . “ ORDER BY $order LIMIT :startRow, :numRows”; Much of this statement will be familiar to you. It ’ s selecting all columns ( * ) from the members table, ordered by the $order variable, and limited to the range specified by the $startRow and $numRows variables. However, there are a couple of concepts here that you haven ’ t seen before. SQL_CALC_FOUND_ROWS is a special MySQL keyword that computes the total number of rows that would be returned by the query, assuming the LIMIT clause wasn ’ t applied. So if the query would return 20 records, but the LIMIT clause limits the returned rows to five, SQL_CALC_FOUND_ROWS returns a value of 20. This is useful because it enables you to display the records over several pages, as you see in a moment. :startRow and :numRows are called placeholders or parameter markers . They serve two purposes. First of all, they let you prepare — that is, get MySQL to parse — a query once, then run it multiple times with different values. If you need to run the same query many times using different input values — when inserting many rows of data, for instance — prepared statements can really speed up execution. Secondly, they reduce the risk of so - called SQL injection attacks. For example, an alternative to using placeholders might be to write: $sql = “SELECT SQL_CALC_FOUND_ROWS * FROM “ . TBL_MEMBERS . “ ORDER BY $order LIMIT $startRow, $numRows”; However, imagine that, due to insufficient checking of user input, a malicious user managed to set $numRows to “ 1; DELETE FROM members ”. This would run the query as intended, but it would also run the second statement, which would delete all records from your members table! When you use placeholders, you pass data to the query via PDO (as you see shortly), not directly into your query string. This allows PDO to check the passed data to ensure that it only contains what it ’ s supposed to contain (integers in this case). The next block of code is inside a try catch construct. This ensures that any PDO exceptions that occur during the query are caught by the method. First, the method calls the prepare() method of the PDO object, passing in the SQL string just created: $st = $conn- > prepare( $sql ); This sets up the query in the MySQL engine, and returns a PDOStatement object to work with (stored in the $st variable). Next, the two :startRow and :numRow placeholders you created earlier are populated with the actual data from the $startRow and $numRow variables: $st- > bindValue( “:startRow”, $startRow, PDO::PARAM_INT ); $st- > bindValue( “:numRows”, $numRows, PDO::PARAM_INT ); c13.indd 392c13.indd 392 9/21/09 9:12:04 AM9/21/09 9:12:04 AM Chapter 13: Retrieving Data from MySQL with PHP 393 The PDOStatement::bindValue() method takes three arguments: the name of the placeholder to bind, the value to use instead of the placeholder, and the data type of the value ( PDO::PARAM_INT , or integer, in this case). By specifying the data type, PDO can ensure that the correct type of data is passed to MySQL. In addition, PDO automatically escapes any quote marks and other special characters in the data. (Failing to escape special characters is another common cause of SQL injection vulnerabilities.) Some other common data types that you can use include: PDO::PARAM_BOOL — A Boolean data type PDO::PARAM_NULL — The NULL data type PDO::PARAM_STR — A string data type. (This is the default if you don ’ t specify a type.) PDO::PARAM_LOB — A LOB data type, such as BLOB or LONGBLOB Now that the statement has been prepared and the placeholders filled with actual values, it ’ s time to run the query: $st- > execute(); The next block of code loops through the record set returned by the query. For each row returned, it creates a corresponding Member object to hold the row ’ s data, and stores the object in an array: $members = array(); foreach ( $st- > fetchAll() as $row ) { $members[] = new Member( $row ); } PDOStatement::fetchAll() is one of many ways that you can retrieve the result set returned from a query. fetchAll() grabs the whole result set in one go, and returns it as an array of associative arrays, where each associative array holds a row of data. Though this is fine for relatively small result sets — say, less than 100 records — be careful of using fetchAll() with large result sets, because the entire result set is loaded into your script ’ s memory in one go. However, in this case fetchAll() is ideal. The script loops through the returned array of rows, passing each $row associative array into the constructor for the Member class. Remember that the constructor is actually in the DataObject class, and it expects an associative array of field names and values, which is exactly what each element of the array returned by fetchAll() contains. The constructor then uses this associative array to populate the Member object with the data. Once the array of Member objects has been created, the method runs another query. Remember the SQL_ CALC_FOUND_ROWS keyword in the original query? To extract the calculated total number of rows, you need to run a second query immediately after the original query: $st = $conn- > query( “SELECT found_rows() AS totalRows” ); $row = $st- > fetch(); The query calls the MySQL found_rows() function to get the calculated total, and returns the result as an alias, totalRows . Notice that this is a regular query that uses PDO::query() , rather than a prepared statement as used by the first query. You don ’ t need to use placeholders because the query doesn ’ t need to contain any passed - in values; hence there is no need to go to the trouble of creating a prepared statement. ❑ ❑ ❑ ❑ c13.indd 393c13.indd 393 9/21/09 9:12:04 AM9/21/09 9:12:04 AM 394 Part III: Using PHP in Practice Finally, the method closes the database connection, then returns the data to the calling code in the form of a two - element array. The first element contains the array of Member objects, and the second element contains the calculated total number of rows: return array( $members, $row[“totalRows”] ); Of course, after the try block comes the corresponding catch block. This simply closes the connection and uses PHP ’ s die() function to abort the script with an error message. The next method, getMember() , works in a similar fashion to getMembers() . It retrieves a single record from the members table, as a Member object. The ID of the record to retrieve is specified by the argument passed to the method. This method creates a prepared statement, much like getMembers() did, to retrieve the record: $sql = “SELECT * FROM “ . TBL_MEMBERS . “ WHERE id = :id”; Next, the $id parameter ’ s value is bound to the :id placeholder, and the query is run: $st- > bindValue( “:id”, $id, PDO::PARAM_INT ); $st- > execute(); If the query returned a row, it is retrieved using the PDOStatement::fetch() method, which retrieves a single row from the result set as an associative array of field names and values. This associative array is then passed to the Member constructor to create and populate a Member object, which is then returned to the calling code after closing the connection: $row = $st- > fetch(); parent::disconnect( $conn ); if ( $row ) return new Member( $row ); The final two convenience methods are used to return the Member object ’ s gender and favoriteGenre fields as human - friendly strings, ideal for displaying in a Web page. getGenderString() simply returns “ Female ” if gender is set to “ f ” , and “ Male ” otherwise. getFavoriteGenreString() looks up the field value in the $_genres array property created at the start of the class in order to return a human - readable form of the value. Building the LogEntry Class The LogEntry class is another data class, much like Member , although it ’ s a fair bit simpler. It retrieves rows of data from the accessLog table. Save the following script as LogEntry.class.php in the book_club folder: < ?php require_once “DataObject.class.php”; class LogEntry extends DataObject { protected $data = array( c13.indd 394c13.indd 394 9/21/09 9:12:05 AM9/21/09 9:12:05 AM Chapter 13: Retrieving Data from MySQL with PHP 395 “memberId” = > “”, “pageUrl” = > “”, “numVisits” = > “”, “lastAccess” = > “” ); public static function getLogEntries( $memberId ) { $conn = parent::connect(); $sql = “SELECT * FROM “ . TBL_ACCESS_LOG . “ WHERE memberId = :memberId ORDER BY lastAccess DESC”; try { $st = $conn- > prepare( $sql ); $st- > bindValue( “:memberId”, $memberId, PDO::PARAM_INT ); $st- > execute(); $logEntries = array(); foreach ( $st- > fetchAll() as $row ) { $logEntries[] = new LogEntry( $row ); } parent::disconnect( $conn ); return $logEntries; } catch ( PDOException $e ) { parent::disconnect( $conn ); die( “Query failed: “ . $e- > getMessage() ); } } } ? > As with Member , the LogEntry class derives from the DataObject abstract class. Its protected $data array contains the field names from the accessLog table: memberId , pageUrl , numVisits , and lastAccess . LogEntry contains just one method, getLogEntries() , that retrieves a list of all accessLog records for a particular member (specified by $memberId ) as LogEntry objects. The query sorts the entries in descending order of access date — that is, newest first: $sql = “SELECT * FROM “ . TBL_ACCESS_LOG . “ WHERE memberId = :memberId ORDER BY lastAccess DESC”; The rest of the method is similar to Member::getMembers() . The statement is prepared, the $memberId parameter is bound to the :memberId placeholder, and the query is run. The record set is retrieved as an array of associative arrays using PDOStatement::fetchAll() , and each associative array is used to create a new LogEntry object, which is then added to an array. The method then returns the array of LogEntry objects to the calling code. Creating the view_members.php Script Now you ’ ve laid all the foundations for your member viewer application; in fact you ’ ve already done most of the hard work. Now it ’ s just a case of writing two scripts: one to display the list of members, and another to display details of an individual member. c13.indd 395c13.indd 395 9/21/09 9:12:05 AM9/21/09 9:12:05 AM 396 Part III: Using PHP in Practice First, the member list. Save the following code as view_members.php in your book_club folder: < ?php require_once( “common.inc.php” ); require_once( “config.php” ); require_once( “Member.class.php” ); $start = isset( $_GET[“start”] ) ? (int)$_GET[“start”] : 0; $order = isset( $_GET[“order”] ) ? preg_replace( “/[^a-zA-Z]/”, “”, $_GET[“order”] ) : “username”; list( $members, $totalRows ) = Member::getMembers( $start, PAGE_SIZE, $order ); displayPageHeader( “View book club members” ); ? > < h2 > Displaying members < ?php echo $start + 1 ? > - < ?php echo min( $start + PAGE_SIZE, $totalRows ) ? > of < ?php echo $totalRows ? > < /h2 > < table cellspacing=”0” style=”width: 30em; border: 1px solid #666;” > < tr > < th > < ?php if ( $order != “username” ) { ? > < a href=”view_members.php? order=username” > < ?php } ? > Username < ?php if ( $order != “username” ) { ? > < /a > < ?php } ? > < /th > < th > < ?php if ( $order != “firstName” ) { ? > < a href=”view_members.php? order=firstName” > < ?php } ? > First name < ?php if ( $order != “firstName” ) { ? > < /a > < ?php } ? > < /th > < th > < ?php if ( $order != “lastName” ) { ? > < a href=”view_members.php? order=lastName” > < ?php } ? > Last name < ?php if ( $order != “lastName” ) { ? > < /a > < ?php } ? > < /th > < /tr > < ?php $rowCount = 0; foreach ( $members as $member ) { $rowCount++; ? > < tr < ?php if ( $rowCount % 2 == 0 ) echo ‘ class=”alt”’ ? > > < td > < a href=”view_member.php?memberId= < ?php echo $member- > getValueEncoded( “id” ) ? > ” > < ?php echo $member- > getValueEncoded( “username” ) ? > < /a > < /td > < td > < ?php echo $member- > getValueEncoded( “firstName” ) ? > < /td > < td > < ?php echo $member- > getValueEncoded( “lastName” ) ? > < /td > < /tr > < ?php } ? > < /table > < div style=”width: 30em; margin-top: 20px; text-align: center;” > < ?php if ( $start > 0 ) { ? > < a href=”view_members.php?start= < ?php echo max( $start - PAGE_SIZE, 0 ) ? > & amp;order= < ?php echo $order ? > ” > Previous page < /a > < ?php } ? > c13.indd 396c13.indd 396 9/21/09 9:12:05 AM9/21/09 9:12:05 AM Chapter 13: Retrieving Data from MySQL with PHP 397 & nbsp; < ?php if ( $start + PAGE_SIZE < $totalRows ) { ? > < a href=”view_members.php?start= < ?php echo min( $start + PAGE_SIZE, $totalRows ) ? > & amp;order= < ?php echo $order ? > ” > Next page < /a > < ?php } ? > < /div > < ?php displayPageFooter(); ? > This script makes use of the Member class to retrieve the list of members from the database, then displays the list in the page. It starts by retrieving two query string parameters — start , representing the record position from which to start displaying the records in the page, and order , which specifies which column to sort the data by — and storing the parameter values in two variables, $start and $order . If either parameter wasn ’ t passed to the script, a default value is used. To improve security, the script filters both parameters to make sure they contain valid values: $start is cast to int , whereas $order uses a regular expression to remove any non - alphabetic characters (because only letters are used for the field names in the members table). You can find out more about regular expressions in Chapter 18. Next, the script retrieves the list of members to display in the page. The code to do this is really simple, because all the complexity is nicely hidden away in the Member class: list( $members, $totalRows ) = Member::getMembers( $start, PAGE_SIZE, $order ); Member::getMembers() is called, passing in the row to start retrieving records from, the number of records to retrieve, and the string to use for the ORDER BY clause. getMembers() dutifully returns a two - element array. The first element — the list of Member objects — is stored in $members , and the second element — the total number of members in the members table — is stored in $totalRows . Now that the data is retrieved, it ’ s simply a case of displaying the list in the page. First, displayPageHeader() is called with an appropriate page title to display the XHTML header. Then an XHTML table element is started, with three header cells for username, first name, and last name: < th > < ?php if ( $order != “username” ) { ? > < a href=”view_members.php?order=username” > < ?php } ? > Username < ?php if ( $order != “username” ) { ? > < /a > < ?php } ? > < /th > < th > < ?php if ( $order != “firstName” ) { ? > < a href=”view_members.php? order=firstName” > < ?php } ? > First name < ?php if ( $order != “firstName” ) { ? > < /a > < ?php } ? > < /th > < th > < ?php if ( $order != “lastName” ) { ? > < a href=”view_members.php? order=lastName” > < ?php } ? > Last name < ?php if ( $order != “lastName” ) { ? > < /a > < ?php } ? > < /th > In each case, the column name is linked to a URL that, when visited, runs view_members.php again with a new order query parameter to sort the data by that column. However, if the data is already sorted by a particular column, that column ’ s name isn ’ t linked, in order to indicate that the data is sorted by that column. c13.indd 397c13.indd 397 9/21/09 9:12:06 AM9/21/09 9:12:06 AM [...]... text-align: center;”> < ?php if ( $start > 0 ) { ?> &order=< ?php echo $order ?>”>Previous page < ?php } ?> < ?php if ( $start + PAGE_SIZE < $totalRows ) { ?> &order=< ?php echo $order ?>”>Next page < ?php } ?> If the current... Retrieving Data from MySQL with PHP Creating the view_member .php Script The very last PHP file you need to create is the script to view an individual member ’s details Save the following code as view_member .php in your book_club folder: < ?php require_once( require_once( require_once( require_once( “common.inc .php ); “config .php ); “Member.class .php ); “LogEntry.class .php ); $memberId = isset( $_GET[“memberId”]...Part III: Using PHP in Practice Next, the data is output, one record per table row: < ?php $rowCount = 0; foreach ( $members as $member ) { $rowCount++; ?> > getValueEncoded( “id” ) ?>”>< ?php echo $member->getValueEncoded( “username” ) ?> < ?php echo $member->getValueEncoded(... Email address < ?php echo $member->getValueEncoded( “emailAddress” ) ?> Other interests < ?php echo $member->getValueEncoded( “otherInterests” ) ?> Access log Web page Number of visits Last visit < ?php 399 Part III: Using PHP in Practice $rowCount... < ?php echo $member->getValueEncoded( “username” ) ?> First name < ?php echo $member->getValueEncoded( “firstName” ) ?> Last name < ?php echo $member->getValueEncoded( “lastName” ) ?> Joined on < ?php echo $member->getValueEncoded( “joinDate” ) ?> Gender < ?php echo $member->getGenderString() ?> Favorite genre < ?php. .. > < ?php echo $logEntry->getValueEncoded( “pageUrl” ) ?> < ?php echo $logEntry->getValueEncoded( “numVisits” ) ?> < ?php echo $logEntry->getValueEncoded( “lastAccess” ) ?> < ?php } ?> Back < ?php. .. for=”favoriteGenre”>What’s your favorite genre? 412 Chapter 14: Manipulating MySQL Data with PHP < ?php foreach ( $member->getGenres() as $value => $label ) { ?> ”< ?php setSelected( $member, “favoriteGenre”, $value ) ?>>< ?php echo $label ?> < ?php } ?> What are your other interests?... the current page relative to the top level of the Web site For example, if a PHP script at http://www.example.com/myscripts/script .php is viewed, $_SERVER[ PHP_ SELF“] is set to /myscripts/script .php For the purposes of this application, you only want to store the filename of the page — for example, diary .php — so you use PHP s basename() function to remove the path portion of the URL You looked at... Manipulating MySQL Data with PHP This is the third and final chapter on the topic of building database-driven PHP applications In Chapter 12 you came to grips with the basics of MySQL and relational databases, and learned how to use PDO (PHP Data Objects) to connect to MySQL databases from PHP Chapter 13 explored the concept of retrieving data from a database within your PHP scripts; you learned in... going to pass in at the time you prepare the statement Find out more on bindParam() in the online PHP manual at http://www php. net/manual/en/pdostatement.bindparam .php 407 Part III: Using PHP in Practice Building a Member Registration Application Now that you know how to insert records into a MySQL table via PHP, you can write a script that lets new members sign up for your book club Rather than reinventing . individual member. c 13. indd 39 5c 13. indd 3 95 9/21/09 9:12: 05 AM9/21/09 9:12: 05 AM 39 6 Part III: Using PHP in Practice First, the member list. Save the following code as view_members .php in your book_club. across pages. c 13. indd 39 8c 13. indd 39 8 9/21/09 9:12: 06 AM9/21/09 9:12: 06 AM Chapter 13: Retrieving Data from MySQL with PHP 39 9 Creating the view_member .php Script The very last PHP file you. < ?php echo max( $start - PAGE_SIZE, 0 ) ? > & amp;order= < ?php echo $order ? > ” > Previous page < /a > < ?php } ? > c 13. indd 39 6c 13. indd 39 6 9/21/09 9:12:05