MySQL /PHP Database Applications Second Edition phần 6 docx

81 335 0
MySQL /PHP Database Applications Second Edition phần 6 docx

Đ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

; drop table if exists story_versions; create table story_versions ( story_id integer not null , modify_dt timestamp , modify_by varchar(20) not null , stage_id integer not null , publish_dt date null , headline varchar(255) null , subtitle varchar(255) null , byline_prefix varchar(20) null , summary text null , body text null , primary key (story_id, modify_dt) , foreign key (story_id) references stories (story_id) on delete cascade ) type=InnoDB ; drop table if exists user_seq; create table user_seq ( id int not null auto_increment , primary key (id) ) type=InnoDB ; drop table if exists user_stage_map; create table user_stage_map ( user_id integer not null , stage_id integer not null , primary key (user_id,stage_id) , index (stage_id,user_id) , foreign key (user_id) references users (user_id) on delete cascade , foreign key (stage_id) references stages (stage_id) on delete cascade ) type=InnoDB ; drop table if exists users; create table users 360 Part IV: Not So Simple Applications ( user_id integer not null auto_increment , username varchar(20) not null , password varchar(16) not null , name varchar(50) not null , email varchar(255) null , primary key (user_id) , unique (username) ) type=InnoDB ; Code Overview At this point, we assume that you are getting comfortable with the way the appli- cations in this book have been constructed. Even with the simple safe_ mysql_query() function in the guestbook example, you saw the usefulness of hav- ing a standard way of working with PHP’s native MySQL routines. The built-in rou- tines will let you do what you need to do, no question. But in the course of using them, you may find that you’re writing the same kind of code multiple times, a sure signal that some higher-level functions are called for. Also, should you ever want to port your code to a different DBMS for some crazy reason, like because you’re being paid to, going through your code and converting those MySQL-specific func- tions to some other system can be a big pain. If you’ve ever done any work with Perl, you may be familiar with the DBI library. It provides a standard interface to multiple database systems. You may have also used Comprehensive Perl Archive Network (CPAN), the big code library where you can find all sorts of previously invented wheels. The same kinds of benefits are available with PHP, thanks to the good people who have built— and are building even now — PEAR. To quote from the PEAR Manifest (http://pear.php.net/manual/en/ introduction.php ): “PEAR is short for ‘PHP Extension and Application Repository’ and is pronounced just like the fruit.” PEAR has several facets. It’s a library of PHP code that solves many common problems encountered by Web developers. It’s also a means of packaging and distributing code, to make it simpler to install code from that library, and to encourage people to share their own code. The best place to find out more is at the Web site: http://pear.php.net. Here you’ll find the code, the main PEAR documentation, mailing lists, and other useful information. PEAR is very much a moving target, undergoing constant improvement and extension, and it has the rough edges that brings. So by way of introduction, we’ll focus on one of the most widely used — and most completely documented — classes, the DB class. It’s one of the core PEAR classes that are automatically distributed and installed as part of PHP (at least, as of this writing). Like Perl’s DBI class, DB Chapter 11: Content-Management System 361 provides a standard interface to multiple database systems. It makes it easy to do the kinds of things you’ll want to do to get data out of a database (like building an associative array from the results of a query) and to put data into a database (like handling those pesky quote marks). As you work through, less and less of the code should require explanation. Thus, our descriptions of the code will deal only with those parts that are really new or tricky. Here, most of the newer looking code will come from assigning the privileges discussed in the previous section. The application sends queries that you haven’t used before. Code Breakdown Once again, the code in this application will make heavy use of the functions in the /functions folder. A lot of the code presented here will make calls to those functions. The great thing about functions is that they become part of your library of code that you can re-use for other purposes. Functions from /dsn The PEAR DB library takes a connection string that will look somewhat familiar if you’ve used Perl’s DBI class, and that is easy to figure out in any case. It typically looks something like this: phptype://username:password@hostspec/database where hostspec might be replaced with the port number and name of the local- host. The routine that accepts connections also accepts an associative array with all the parts spelled out as key/value pairs, so that’s what we’ll use. Rather than store usernames and passwords in the code of the example, as we have done up until now, we’ve moved the connection information for the database to a function in a separate directory, outside the document root of the Apache server. This provides a small amount of extra security — though if you’re on a shared server, this information is still vulnerable. But at least moving it out of the Web-server document root means that no one can download the file as a Web page. In our setup, the /dsn directory is parallel to the /htdocs directory. In there is one file, db_dsnlist.php, defining one function, db_dsnlist(): function db_dsnlist() { static $_defaults = array( ‘application’ => ‘default’ ); static $_simple = array( 362 Part IV: Not So Simple Applications ‘application’, ‘username’, ‘password’, ‘database’ ); $p = func_get_args(); $p = parse_arguments($p, $_simple, $_defaults); static $dsnlist = array( ‘default’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => ‘nobody’ , ‘password’ => ‘ydobon’ , ‘protocol’ => ‘tcp’ , ‘hostspec’ => ‘localhost’ , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘test’ ) , ‘oldcatalog’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => ‘nobody’ , ‘password’ => ‘ydobon’ , ‘protocol’ => ‘tcp’ , ‘hostspec’ => ‘localhost’ , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘oldcatalog’ ) , ‘catalog’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => ‘nobody’ , ‘password’ => ‘ydobon’ , ‘protocol’ => ‘tcp’ , ‘hostspec’ => ‘localhost’ , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘catalog’ ) , ‘discussion’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => ‘nobody’ , ‘password’ => ‘ydobon’ , ‘protocol’ => ‘tcp’ Chapter 11: Content-Management System 363 , ‘hostspec’ => ‘localhost’ , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘discussion’ ) , ‘netsloth’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => ‘nobody’ , ‘password’ => ‘ydobon’ , ‘protocol’ => ‘tcp’ , ‘hostspec’ => ‘localhost’ , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘netsloth’ ) , ‘content’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => NULL , ‘password’ => NULL , ‘protocol’ => ‘tcp’ , ‘hostspec’ => ‘localhost’ , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘netsloth’ ) , ‘admin’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => ‘admin’ , ‘password’ => ‘supersecret’ , ‘protocol’ => ‘tcp’ , ‘hostspec’ => ‘localhost’ , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘netsloth’ ) , ‘tracking’ => array( ‘phptype’ => ‘mysql’ , ‘dbsyntax’ => NULL , ‘username’ => ‘nobody’ , ‘password’ => ‘ydobon’ , ‘protocol’ => ‘tcp’ , ‘hostspec’ => ‘localhost’ 364 Part IV: Not So Simple Applications , ‘port’ => NULL , ‘socket’ => NULL , ‘database’ => ‘tracking’ ) ); // remove NULL values to not override entries from dsn $p = array_diff($p, array_filter($p,’is_null’)); if (isset($dsnlist[$p[‘application’]])) { $dsn = array_merge($dsnlist[$p[‘application’]],$p); } else { $dsn = array_merge($dsnlist[‘default’],$p); } return $dsn; } Typically, this function is called with just the application name as a parameter, and will return the entry for that application from the static array of connection parameters. But we can pass in other values as well, which are merged into the returned array. Functions from /book/functions/database The functions of the PEAR DB library are powerful enough that in most circum- stances you can use them either directly in the code of the Web pages or in functions specific to an example. In a few instances you do the same work in all the examples, though, and these general functions are stored in the /databases directory of the general /functions directory. db_connect() The db_connect() function is similar to the mysql_connect() function we used in previous examples. It creates a persistent connection to the MySQL server, getting connection parameters from the db_dsnlist() function described earlier. function db_connect() { static $_connections = array(); static $_defaults = array( ‘application’ => NULL , ‘database’ => NULL , ‘username’ => NULL , ‘db_error_level’ => E_USER_ERROR , ‘db_error_handler’ => ‘db_error_handler’ Chapter 11: Content-Management System 365 , ‘options’ => array( ‘debug’ => 4 , ‘persistent’ => TRUE , ‘autofree’ => TRUE ) ); static $_simple = array(‘application’,’username’,’password’); $dc = count($_connections); $p = func_get_args(); if (empty($p)) { if ($dc) { $dbh = array_pop(array_values($_connections)); if ($dbh === NULL) { user_error(‘Last connection is NULL.’, E_USER_ERROR); exit; } return $dbh; } user_error(‘No existing database connection found.’, E_USER_ERROR); exit; } $p = parse_arguments($p, $_simple, $_defaults); if (empty($p[‘application’])) { $p[‘application’] = $p[‘database’]; if (!empty($p[‘username’])) { $p[‘application’] .= ‘:’.$p[‘username’]; } } $dbh = array_key_value($_connections,$p[‘application’],NULL); if ($dbh !== NULL) { return $dbh; } $dsn = db_dsnlist($p); $dbh = DB::connect($dsn, $p[‘options’]); if (DB::isError($dbh)) { $private_error = ‘dsn:’.var_export($dsn,TRUE).”\n” 366 Part IV: Not So Simple Applications .’ error:’.var_export($dbh,TRUE).”\n” ; user_error( ‘Could not connect to database: ‘.$dbh->getMessage() , $p[‘db_error_level’] ); return FALSE; } if (is_string($p[‘db_error_handler’]) && function_exists($p[‘db_error_handler’]) ) { // it’s a function name - OK } elseif (is_array($p[‘db_error_handler’]) && count($p[‘db_error_handler’]) == 2 && method_exists($p[‘db_error_handler’][0], $p[‘db_error_handler’][1]) ) { // it’s an object method - OK } else { $p[‘db_error_handler’] = NULL; } if (!empty($p[‘db_error_handler’])) { $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, $p[‘db_error_handler’]); } else { $dbh- >setErrorHandling(PEAR_ERROR_TRIGGER,$p[‘db_error_level’]); } $_connections[$p[‘application’]] = $dbh; if ($dbh === NULL) { $private_error = var_export($_connection, TRUE); user_error(‘connection is NULL.’, $p[‘db_error_level’]); exit; } return $dbh; } Chapter 11: Content-Management System 367 If db_connect() is called with no parameters, it hands back the handle of the last DB object that was created. You’ll notice the use of this function throughout this example and the examples that follow; we can call db_connect() from any point in the application — in a Web page, inside a function, and so on— and get access to the database, without having to set up a global variable, and without making multiple connections. The more advanced object-oriented features of PHP 4.3 even let us do away with storing the object handle in a variable, and just use the function in its place. Prior to PHP 4.3 we would have to do something like this: $dbh = db_connect(); $dbh->query(‘delete * from mysql.users’); But the new PHP object handling lets us just write db_connect()->query(‘delete * from mysql.users’); The db_connect() function also sets up how DB errors are handled. They can either be passed on directly to a function or class method, or processed when they trigger a PHP error of a given error level and thus go through whatever error handling we’ve set up for general PHP errors. For the examples in this book, we normally use the former method, passing DB errors on to a function of our own, db_error_handler(). db_error_handler() We use a special error-handling function for DB errors rather than only relying on our regular error_handler() function. We do this so that we can roll back any open transaction (if we still have an active database connection) and then trigger a fatal error that will exit the page and stop any other queries from running. This is key to the concept of atomic transactions, which are multi-stage procedures in which, by rule, either all of the steps must occur, or none of them. This prevents such problems as, in the case of a bank, money being credited to one account with- out being subtracted from another one. function db_error_handler($db_error) { $timestamp = time(); // this should be unnecessary but can’t hurt $dbh = db_connect(); if (is_a($dbh,’DB’)) { $last_query = $dbh->last_query; $dbh->query(‘rollback’); } $skip_past_function = ‘mysqlraiseerror’; $private_error = “DB error ($timestamp): “.$db_error->userinfo; 368 Part IV: Not So Simple Applications $error_level = E_USER_ERROR; user_error( “Database error - please contact the system administrator.($timestamp)” ,$error_level ); } db_fetch_record() This function provides a convenient way to get a record or set of records from a table. It makes use of DB’s system for token replacement, which is a fancy way of saying “placeholders.” As a simple example, you can run a query with DB like this: $result = $dbh->query(‘select * from mytable where mykey = 1’); But you can also pass in two arguments to DB::query(), the query string itself, and an array of values to replace into the string: $result = $dbh->query( ‘select * from mytable where mykey = ?’ , array($mykey) ); The token character ? in the query string tells DB that it should replace it with the content of a value from the array of arguments. If you have two ? characters in your query string, it looks for two values in the array, and so on. The very nice aspect of this — beyond freeing you from having to build a new query string for every new set of values you want to include in your query, which is no small pota- toes — is that DB takes care of quoting and escaping characters for you. A statement like this: $mykey = 1; $myname = “O’Reilly”; $result = $dbh->query( ‘select * from mytable where mykey = ? and myname = ?’ , array($mykey, $myname) ); results in this query being run by MySQL: select * from mytable where mykey = 1 and myname = ‘O\’Reilly’ and although this book is about PHP and MySQL, it’s worth noting here that DB can be used with a wide variety of databases, handling the proper quotation and escape syntax for each one. If you’ve ever had to port code from, say, Sybase or PostgreSQL to MySQL, you can appreciate how valuable a feature that is. Chapter 11: Content-Management System 369 [...]... user/’, db_connect()>error_message)) { $error = “Access denied to $stage for $username”; } else { $error = Database error - could not update stage’; } user_error($error, E_USER_ERROR); } if (!empty($publish_yr) && !empty($publish_mn) && !empty($publish_dy)) { 385 3 86 Part IV: Not So Simple Applications // build a publish date from the three related select // fields in the form, if all three were set... logging in to the database with his or her own username and password The script that performs this login will need to be just a touch more flexible than the one we used in the other applications This application is going to use the same authentication methods seen in the previous examples, but here the values for $PHP_AUTH_USER and $PHP_AUTH_PW will also be the values used to log in to the database 387... are front ends to the main stages() function The first time stages() is called, the contents of the stages table from the database are loaded into a static array This enables us to make subsequent calls to look up a stage name by its ID value, or vice versa, without querying the database . (DB::isError($dbh)) { $private_error = ‘dsn:’.var_export($dsn,TRUE).” ” 366 Part IV: Not So Simple Applications .’ error:’.var_export($dbh,TRUE).” ” ; user_error( ‘Could not connect to database: ‘.$dbh->getMessage() , $p[‘db_error_level’] ); return. $dbh->last_query; $dbh->query(‘rollback’); } $skip_past_function = ‘mysqlraiseerror’; $private_error = “DB error ($timestamp): “.$db_error->userinfo; 368 Part IV: Not So Simple Applications $error_level = E_USER_ERROR; user_error( Database error. array( ‘application’ => ‘default’ ); static $_simple = array( 362 Part IV: Not So Simple Applications ‘application’, ‘username’, ‘password’, database ); $p = func_get_args(); $p = parse_arguments($p,

Ngày đăng: 12/08/2014, 21:20

Từ khóa liên quan

Mục lục

  • Part IV Not So Simple Applications

    • 11 Content-Management System

      • Code Overview

      • Code Breakdown

        • Functions from / dsn

        • Functions from / book/ functions/ database

        • Functions from / content/ functions

        • Interesting Code Flow

          • content/ authenticate. php

          • content/ admin/ user. php

          • content/ story. php

          • Summary

          • 12 Catalog

            • Determining the Scope and Goals of

            • Determining the Scope and Goals of the Application

              • Necessary pages

              • What do we need to prevent?

              • The Data

              • Code Overview

                • The object- oriented approach

                • Accessing the file system

                • Uploading files

                • Code Breakdown

                  • Objects in theory

                  • Classes

                  • Sample script

                  • Summary

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan