`
isiqi
  • 浏览: 16499100 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

用 PHP 走向动态

阅读更多
Jack Herrington (jack_d_herrington@codegeneration.net), 高级软件工程师, "Code Generation Network"
2006 4 27
PHP V5 新的面向对象编程特性显著提升了这个流行语言中的功能层次。学习如何用 PHP V5 动态特性创建可以满足需求的对象。
PHP V5 中新的面向对象编程(OOP)特性的引入显著提升了这个编程语言的功能层次。现在不仅有了私有的、受保护的和公共的成员变量和函数 —— 就像在 Java™ C++ C# 编程语言中一样 —— 但是还可以创建在运行时变化的对象,即动态地创建新方法和成员变量。而使用 JavaC++ C# 语言是做不到这件事的。这种功能使得超级快速的应用程序开发系统(例如 Ruby on Rails)成为可能。
但是,在进入这些之前,有一点要注意:本文介绍 PHP V5 中非常高级的 OOP 特性的使用,但是这类特性不是在每个应用程序中都需要的。而且,如果不具备 OOP 的坚实基础以及 PHP 对象语法的初步知识,这类特性将会很难理解。
对象是把双刃剑。一方面,对象是封装数据和逻辑并创建更容易维护的系统的重大方式。但另一方面,它们会变得很繁琐,需要许多冗余的代码,这时可能最希望做到的就是不要犯错。这类问题的一个示例来自数据库访问对象。一般来说,想用一个类代表每个数据库表,并执行以下功能:对象从数据库读出数据行;允许更新字段,然后用新数据更新数据库或删除行。还有一种方法可以创建新的空对象,设置对象的字段,并把数据插入数据库。
如果在数据库中有一个表,名为 Customers,那么就应当有一个对象,名为Customer,它应当拥有来自表的字段,并代表一个客户。而且Customer对象应当允许插入、更新或删除数据库中对应的记录。现在,一切都很好,而且有也很多意义。但是,有许多代码要编写。如果在数据库中有 20 个表,就需要 20 个类。
有三个解决方案可以采用。第一个解决方案就是,坐在键盘前,老老实实地录入一段时间。对于小项目来说,这还可以,但是我很懒。第二个解决方案是用代码生成器,读取数据库模式,并自动编写代码。这是个好主意,而且是另一篇文章的主题。第三个解决方案,也是我在本文中介绍的,是编写一个类,在运行时动态地把自己塑造成指定表的字段。这个类执行起来比起特定于表的类可能有点慢 —— 但是把我从编写大量代码中解脱出来。这个解决方案在项目开始的时候特别有用,因为这时表和字段不断地变化,所以跟上迅速的变化是至关重要的。
所以,如何才能编写一个能够弯曲的类呢?
对象有两个方面:成员变量方法。在编译语言(例如 Java)中,如果想调用不存在的方法或引用不存在的成员变量,会得到编译时错误。但是,在非编译语言,例如 PHP 中,会发生什么?
PHP 中的方法调用是这样工作的。首先,PHP 解释器在类上查找方法。如果方法存在,PHP 就调用它。如果没有,那么就调用类上的魔法方法__call(如果这个方法存在的话)。如果__call失败,就调用父类方法,依此类推。
__call方法有两个参数:被请求的方法的名称和方法参数。如果创建的__call方法接受这两个参数,执行某项功能,然后返回 TRUE,那么调用这个对象的代码就永远不会知道在有代码的方法和__call机制处理的方法之间的区别。通过这种方式,可以创建这样的对象,即动态地模拟拥有无数方法的情况。
除了__call方法,其他魔法方法 —— 包括__get__set —— 调用它们的时候,都是因为引用了不存在的实例变量。脑子里有了这个概念之后,就可以开始编写能够适应任何表的动态数据库访问类了。
魔法方法
魔法方法是有特定名称的方法,PHP 解释器在脚本执行的特定点上会查找魔法方法。最常见的魔法方法就是对象创始时调用的构造函数。
先从一个简单的数据库模式开始。清单 1 所示的模式针对的是单一的数据表数据库,容纳图书列表。

清单 1. MySQL 数据库模式
DROP TABLE IF EXISTS book;
CREATE TABLE book (
book_id INT NOT NULL AUTO_INCREMENT,
title TEXT,
publisher TEXT,
author TEXT,
PRIMARY KEY( book_id )
);
请把这个模式装入到名为 bookdb 的数据库。
接下来,编写一个常规的数据库类,然后再把它修改成动态的。清单 2 显示了图书表的简单的数据库访问类。

清单 2. 基本的数据库访问客户机
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/bookdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
class Book
{
private $book_id;
private $title;
private $author;
private $publisher;
function __construct()
{
}
function set_title( $title ) { $this->title = $title; }
function get_title( ) { return $this->title; }
function set_author( $author ) { $this->author = $author; }
function get_author( ) { return $this->author; }
function set_publisher( $publisher ) {
$this->publisher = $publisher; }
function get_publisher( ) { return $this->publisher; }
function load( $id )
{
global $db;
$res = $db->query( "SELECT * FROM book WHERE book_id=?",
array( $id ) );
$res->fetchInto( $row, DB_FETCHMODE_ASSOC );
$this->book_id = $id;
$this->title = $row['title'];
$this->author = $row['author'];
$this->publisher = $row['publisher'];
}
function insert()
{
global $db;
$sth = $db->prepare(
'INSERT INTO book ( book_id, title, author, publisher )
VALUES ( 0, ?, ?, ? )'
);
$db->execute( $sth,
array( $this->title,
$this->author,
$this->publisher ) );
$res = $db->query( "SELECT last_insert_id()" );
$res->fetchInto( $row );
return $row[0];
}
function update()
{
global $db;
$sth = $db->prepare(
'UPDATE book SET title=?, author=?, publisher=?
WHERE book_id=?'
);
$db->execute( $sth,
array( $this->title,
$this->author,
$this->publisher,
$this->book_id ) );
}
function delete()
{
global $db;
$sth = $db->prepare(
'DELETE FROM book WHERE book_id=?'
);
$db->execute( $sth,
array( $this->book_id ) );
}
function delete_all()
{
global $db;
$sth = $db->prepare( 'DELETE FROM book' );
$db->execute( $sth );
}
}
$book = new Book();
$book->delete_all();
$book->set_title( "PHP Hacks" );
$book->set_author( "Jack Herrington" );
$book->set_publisher( "O'Reilly" );
$id = $book->insert();
echo ( "New book id = $id\n" );
$book2 = new Book();
$book2->load( $id );
echo( "Title = ".$book2->get_title()."\n" );
$book2->delete( );
?>
为了保持代码简单,我把类和测试代码放在一个文件中。文件首先得到数据库句柄,句柄保存在一个全局变量中。然后定义Book类,用私有成员变量代表每个字段。还包含了一套用来从数据库装入、插入、更新和删除行的方法。
底部的测试代码先删除数据库中的所有条目。然后,代码插入一本书,输出新记录的 ID。然后,代码把这本书装入另一个对象并输出书名。
清单 3 显示了在命令行上用 PHP 解释器运行代码的效果。

清单 3. 在命令行运行代码
% php db1.php
New book id = 25
Title = PHP Hacks
%
不需要看太多,就已经得到重点了。Book对象代表图书数据表中的行。通过使用上面的字段和方法,可以创建新行、更新行和删除行。
下一步是让类变得稍微动态一些:动态地为每个字段创建get_set_方法。清单 4 显示了更新后的代码。

清单 4. 动态 get_ set_ 方法
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/bookdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
class Book
{
private $book_id;
private $fields = array();
function __construct()
{
$this->fields[ 'title' ] = null;
$this->fields[ 'author' ] = null;
$this->fields[ 'publisher' ] = null;
}
function __call( $method, $args )
{
if ( preg_match( "/set_(.*)/", $method, $found ) )
{
if ( array_key_exists( $found[1], $this->fields ) )
{
$this->fields[ $found[1] ] = $args[0];
return true;
}
}
else if ( preg_match( "/get_(.*)/", $method, $found ) )
{
if ( array_key_exists( $found[1], $this->fields ) )
{
return $this->fields[ $found[1] ];
}
}
return false;
}
function load( $id )
{
global $db;
$res = $db->query( "SELECT * FROM book WHERE book_id=?",
array( $id ) );
$res->fetchInto( $row, DB_FETCHMODE_ASSOC );
$this->book_id = $id;
$this->set_title( $row['title'] );
$this->set_author( $row['author'] );
$this->set_publisher( $row['publisher'] );
}
function insert()
{
global $db;
$sth = $db->prepare(
'INSERT INTO book ( book_id, title, author, publisher )
VALUES ( 0, ?, ?, ? )'
);
$db->execute( $sth,
array( $this->get_title(),
$this->get_author(),
$this->get_publisher() ) );
$res = $db->query( "SELECT last_insert_id()" );
$res->fetchInto( $row );
return $row[0];
}
function update()
{
global $db;
$sth = $db->prepare(
'UPDATE book SET title=?, author=?, publisher=?
WHERE book_id=?'
);
$db->execute( $sth,
array( $this->get_title(),
$this->get_author(),
$this->get_publisher(),
$this->book_id ) );
}
function delete()
{
global $db;
$sth = $db->prepare(
'DELETE FROM book WHERE book_id=?'
);
$db->execute( $sth,
array( $this->book_id ) );
}
function delete_all()
{
global $db;
$sth = $db->prepare( 'DELETE FROM book' );
$db->execute( $sth );
}
}
..
要做这个变化,需要做两件事。首先,必须把字段从单个实例变量修改成字段和值组合构成的散列表。然后必须添加一个__call方法,它只查看方法名称,看方法是set_还是get_方法,然后在散列表中设置适当的字段。
注意,load方法通过调用set_titleset_authorset_publisher方法 —— 实际上都不存在 —— 来实际使用__call方法。
删除get_set_方法只是一个起点。要创建完全动态的数据库对象,必须向类提供表和字段的名称,还不能有硬编码的引用。清单 5 显示了这个变化。

清单 5. 完全动态的数据库对象类
<?php
require_once("DB.php");
$dsn = 'mysql://root:password@localhost/bookdb';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
class DBObject
{
private $id = 0;
private $table;
private $fields = array();
function __construct( $table, $fields )
{
$this->table = $table;
foreach( $fields as $key )
$this->fields[ $key ] = null;
}
function __call( $method, $args )
{
if ( preg_match( "/set_(.*)/", $method, $found ) )
{
if ( array_key_exists( $found[1], $this->fields ) )
{
$this->fields[ $found[1] ] = $args[0];
return true;
}
}
else if ( preg_match( "/get_(.*)/", $method, $found ) )
{
if ( array_key_exists( $found[1], $this->fields ) )
{
return $this->fields[ $found[1] ];
}
}
return false;
}
function load( $id )
{
global $db;
$res = $db->query(
"SELECT * FROM ".$this->table." WHERE ".
$this->table."_id=?",
array( $id )
);
$res->fetchInto( $row, DB_FETCHMODE_ASSOC );
$this->id = $id;
foreach( array_keys( $row ) as $key )
$this->fields[ $key ] = $row[ $key ];
}
function insert()
{
global $db;
$fields = $this->table."_id, ";
$fields .= join( ", ", array_keys( $this->fields ) );
$inspoints = array( "0" );
foreach( array_keys( $this->fields ) as $field )
$inspoints []= "?";
$inspt = join( ", ", $inspoints );
$sql = "INSERT INTO ".$this->table." ( $fields )
VALUES ( $inspt )";
$values = array();
foreach( array_keys( $this->fields ) as $field )
$values []= $this->fields[ $field ];
$sth = $db->prepare( $sql );
$db->execute( $sth, $values );
$res = $db->query( "SELECT last_insert_id()" );
$res->fetchInto( $row );
$this->id = $row[0];
return $row[0];
}
function update()
{
global $db;
$sets = array();
$values = array();
foreach( array_keys( $this->fields ) as $field )
{
$sets []= $field.'=?';
$values []= $this->fields[ $field ];
}
$set = join( ", ", $sets );
$values []= $this->id;
$sql = 'UPDATE '.$this->table.' SET '.$set.
' WHERE '.$this->table.'_id=?';

相关推荐

    基于php的员工绩效考核系统(帮老姐公司开发).zip

    首先,我们要明确的是,这个系统是用PHP编程语言构建的,这是一种广泛应用于Web开发的服务器端脚本语言,以其易学易用、灵活性高、跨平台性强的特点深受开发者喜爱。PHP与HTML代码可以无缝嵌入,使得动态网页的创建...

    php网页文字游戏

    PHP是一种广泛使用的开源脚本语言,特别适合于Web开发,可以嵌入到HTML中直接执行。在这款游戏中,PHP可能被用来创建动态页面,根据用户的选择生成不同的游戏结果。例如,玩家的决定可能会影响角色的发展路径、剧情...

    PHP开发实战1200例

    接下来,书中将探讨PHP与HTML的交互,讲解如何创建动态网页。此外,还会详细介绍PHP的表单处理,包括GET和POST方法,以及如何验证用户输入,确保数据安全。在文件操作部分,读者将学习如何读写文件、上传下载文件,...

    教师信息管理系统PHP+MySQL

    在信息化飞速发展的今天,教育领域的管理也逐渐走向数字化、网络化。"教师信息管理系统PHP+MySQL"是一个典型的信息管理平台,旨在高效、便捷地处理教师相关的数据。这个系统是作者的毕业设计作品,通过PHP编程语言与...

    php+sql学生宿舍管理系统

    总的来说,"PHP+SQL学生宿舍管理系统"通过结合PHP的动态网页开发能力和MySQL的数据库管理优势,为高校宿舍管理带来了现代化的管理手段,提高了工作效率,降低了人为错误,是当前数字化校园建设中不可或缺的一部分。...

    基于PHP学校管理系统原代码

    在信息化飞速发展的今天,教育领域的管理也逐渐走向数字化、智能化。"基于PHP的学校管理系统"是这样的一个工具,它利用PHP这一广泛使用的服务器端脚本语言,为学校提供了一套全面、高效的管理解决方案。本文将深入...

    PHP5与MySQL5从入门到精通

    《PHP5与MySQL5从入门到精通》是一本专注于介绍如何使用PHP5和MySQL5技术进行动态网页开发的书籍。本书是为初学者和希望提高自身技能的中级开发者所准备的。它不仅覆盖了PHP和MySQL的基础知识,还深入探讨了这两项...

    2011PHP技术高峰论坛演讲-张宴

    3. PHP Web开发:2011年的Web开发趋势可能包括MVC模式的应用、模板引擎的使用,以及如何利用PHP构建动态网站。 4. PHP框架:那时可能流行的PHP框架有Zend Framework、CodeIgniter、Yii等,张宴可能会讨论这些框架的...

    php项目之基于H5的宿舍管理系统(源码).zip

    前端使用HTML5和CSS3进行页面布局和美化,后端使用PHP处理业务逻辑,数据库采用MySQL进行数据存储。这种架构具有良好的扩展性和维护性,降低了系统的运行成本。 四、功能模块 1. 用户管理:包括管理员和学生用户的...

    php+mysql 网页开发从零起步

    《PHP+MySQL网页开发从零起步》是一本旨在引导初学者入门PHP和MySQL结合的网页开发的教程。这本书的核心目标是教会读者如何利用这两种强大的...通过深入学习和实践,开发者可以进一步提升技能,走向专业化的开发道路。

    基于php建筑工程装修施工网站.zip

    在数字化时代,建筑行业的管理与服务也逐渐走向线上化。本文将深入探讨一个基于PHP构建的建筑工程装修施工网站,它整合了建筑、地产领域的诸多功能,旨在提升工程项目的管理和沟通效率。本文将从PHP语言的基础特性、...

    PHP实例开发源码—雷风影视系统(LFCMS) php版.zip

    PHP是一种服务器端的脚本语言,它嵌入到HTML代码中,用于处理动态网页内容。理解PHP的基础语法、变量、数据类型、流程控制、函数和类是学习LFCMS源码的前提。例如,PHP中的数组、字符串操作以及面向对象编程(OOP)...

    php+mysql学生成绩管理系统.zip

    在当今信息化时代,教育领域的管理也逐渐走向数字化,PHP+MySQL作为一款高效、易用且开源的Web开发组合,被广泛应用于构建各类信息系统,其中包括学生成绩管理系统。这个系统的核心功能是记录、统计和分析学生的学习...

    library-mysql.rar_图书_图书 php_图书管理 php_图书馆_基于php Mysql

    PHP是一种广泛使用的服务器端脚本语言,尤其在网页开发领域有着广泛的应用。它与HTML紧密集成,能够轻松创建动态交互式的网页。在图书馆管理系统中,PHP主要负责处理用户请求,与数据库进行交互,以及呈现后台处理后...

    基于PHP+H5的宿舍管理系统源码.zip

    在当今数字化时代,高校宿舍管理也逐渐走向信息化,基于PHP和HTML5技术的宿舍管理系统是实现这一目标的重要工具。本系统源码提供了一种高效、便捷的方式来处理宿舍分配、信息查询、问题报告等一系列管理任务。下面将...

    一种基于PHP教材管理系统的设计与实现(含php源码和文章说明和数据库)

    在本教材管理系统中,PHP主要负责处理用户请求、与数据库交互以及动态生成页面内容。 二、MySQL数据库 1. 数据类型:MySQL数据库支持多种数据类型,如数值型(INT、FLOAT、DECIMAL等)、字符串型(VARCHAR、TEXT等...

    供求信息网PHP+Mysql

    PHP语法简洁,易于学习,可以嵌入到HTML中,用于动态生成网页内容。在供求信息网中,PHP主要用于处理用户请求,如搜索、发布、更新和删除供求信息,以及用户的登录、注册等操作。 接着,MySQL作为关系型数据库管理...

    php考试题目,初级用户必备

    此扩展名用于动态链接库文件。 - **正确答案**:A。 **16、c。在HTML中,()标记符用来插入图片** - **选项分析**: - A. body. 此标记用于定义文档的主体部分。 - B. title. 此标记用于定义文档的标题。 - C. ...

    基于HTML5的校园网高校管理系统的设计与实现.pdf

    PHP 技术可以与 Linux 操作系统结合,使用大量免费的应用软件,实现一个支持数据库的源码开放的动态网页系统开发平台。 3. MS SqlServer2005 数据库技术:MS SqlServer2005 数据库技术是一个高效、可靠的数据库管理...

Global site tag (gtag.js) - Google Analytics