Tài liệu PHP Objects, Patterns and Practice- P8 pptx

50 465 0
Tài liệu PHP Objects, Patterns and Practice- P8 pptx

Đ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

CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 329 relevant channels have been discovered before running channel-discover with the -o (optional dependencies) flag set. Using a PEAR Package Once you have installed a PEAR package, you should be able to use it in your projects immediately. Your PEAR directory should already be in your include path—there should be no problem including the package once it has been installed. Let’s install PEAR_Config and any dependencies it might have: $ pear install -a Config downloading Config-1.10.11.tgz . Starting to download Config-1.10.11.tgz (27,718 bytes) .done: 27,718 bytes downloading XML_Parser-1.2.8.tgz . Starting to download XML_Parser-1.2.8.tgz (13,476 bytes) .done: 13,476 bytes install ok: channel://pear.php.net/Config-1.10.11 install ok: channel://pear.php.net/XML_Parser-1.2.8 Here’s how you would include the package: require_once("Config.php"); class MyConfig { private $rootObj; function __construct( $filename=null, $type='xml' ) { $this->type=$type; $conf = new Config(); if ( ! is_null( $filename ) ) { $this->rootObj = $conf->parseConfig($filename, $type); } else { $this->rootObj = new Config_Container( 'section', 'config' ); $conf->setroot($this->rootObj); } } function set( $secname, $key, $val ) { $section=$this->getOrCreate( $this->rootObj, $secname ); $directive=$this->getOrCreate( $section, $key, $val ); $directive->setContent( $val ); } private function getOrCreate( Config_Container $cont, $name, $value=null ) { $itemtype=is_null( $value )?'section':'directive'; if ( $child = $cont->searchPath( array($name) ) ) { return $child; } return $cont->createItem( $itemtype, $name, null ); } function __toString() { return $this->rootObj->toString( $this->type ); Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 330 } } We begin by including Config.php. Most PEAR packages work in this way, providing a single top- level point of access. All further require statements are then made by the package itself. The rest of the example simply works with the classes provided by the Config package: Config and Config_Container. The Config package lets you access and create configuration files in a variety of formats. This simple MyConfig class uses Config to work with configuration data. Here’s a quick usage example: $myconf = new MyConfig(); $myconf->set("directories", "prefs", "/tmp/myapp/prefs" ); $myconf->set("directories", "scratch", "/tmp/" ); $myconf->set("general", "version", "1.0" ); echo $myconf; By default, this generates output in XML format: <config> <directories> <prefs>/tmp/myapp/prefs</prefs> <scratch>/tmp/</scratch> </directories> <general> <version>1.0</version> </general> </config> As is often the case with sample code, this class is incomplete—it still requires additional error checking as well as methods for writing the configuration data to file. Still, it is pretty useful already, thanks to the power of the PEAR package. By passing different type strings to Config, we could have rendered the previous output in various configuration formats (like the INI format that the PHP application itself uses, for example).Of course, the details of the Config package are beyond the scope of this chapter. The good news is that for official PEAR packages, you will find API instructions on the web site at http://pear.php.net/. In all cases, you should expect to be able to add the functionality of a PEAR package to your script with minimal effort. The package should provide you with a clear, well-documented API. ■ Note The bad news about PEAR packages is that the struggle to support older versions of PHP is extremely hard to square with the demands of later versions. Like many PEAR packages, Config now relies on deprecated language features, which cannot be easily discarded for the sake of backward compatibility. In order to turn off warnings about this, you can set an error_reporting directive like this: error_reporting = E_ALL & ~E_DEPRECATED in your php.ini file. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 331 Handling PEAR Errors Many, if not most, official PEAR packages use the standard PEAR error class PEAR_Error. This is often returned in place of the expected value if something goes wrong in an operation. This behavior should be documented, and you can test return values using the static PEAR::isError() method. $this->rootObj = @$conf->parseConfig($filename, $type); if ( PEAR::isError( $this->rootObj ) ) { print "message: ". $this->rootObj->getMessage() ."\n"; print "code: ". $this->rootObj->getCode() ."\n\n"; print "Backtrace:\n"; foreach ( $this->rootObj->getBacktrace() as $caller ) { print $caller['class'].$caller['type']; print $caller['function']."() "; print "line ".$caller['line']."\n"; } die; } Here, I test the return value from Config::parseConfig(). PEAR::isError( $this->rootObj ) is the functional equivalent of $this->rootObj instanceof PEAR_Error So within my conditional block, I know that $this->rootObj is a PEAR_Error rather than a Config_Container object. Once I am sure I have a PEAR_Error object, I can interrogate it for more information about the error. In my example, I have three of the most useful methods: getMessage() returns a message that describes the error; getCode() returns an integer corresponding to the error type (this is an arbitrary number that the package author will have declared as a constant and, we hope, documented); and finally, getBacktrace() returns an array of the methods and classes that lead to the error. This enables us to work our way back through our script’s operation and locate the root cause of the error. As you can see, getBacktrace() is itself an array, which describes each method or function that led to the error. The elements are described in Table 15–1. Table 15–1. Fields Provided by PEAR_Error::getBacktrace() Field Description file Full path to PHP file args The arguments passed to the method or function class The name of the class (if in class context) function The name of the function or method type If in class context, the nature of the method call (:: or ->) line The line number Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 332 The way that PEAR_Error pollutes a method’s return value was an unfortunate necessity before the advent of PHP 5. With PHP 4 at or near the end of its life, it’s no surprise that PEAR_Error has been deprecated. Although many packages continue to use PEAR_Error and will probably do so for some time, more are beginning to use PEAR_Exception. If you were to use the XML_Feed_Parser package, for example you would be catching exceptions rather than testing return types: $source="notthere"; try { $myfeed = new XML_Feed_Parser( $source ); } catch ( XML_Feed_Parser_Exception $e ) { print "message: ". $e->getMessage() ."\n"; print "code: ". $e->getCode() ."\n"; print "error class: ". $e->getErrorClass() ."\n"; print "error method: ".$e->getErrorMethod() ."\n"; print "trace: ". $e->getTraceAsString()."\n"; print "error data: "; print_r( $e->getErrorData() ); } Typically. a PEAR package will extend PEAR_Exception, partly so that it can add any functionality it needs, but mainly so that you can use your catch clause to distinguish between Exception types. PEAR_Exception, of course, itself extends Exception, so you get the standard methods I covered in Chapter 4. You also benefit from some additions. getErrorClass() and getErrorMethod(), for example, tell you the class and method from which the error originated. getErrorData() may include additional error information in an associative array, although this is left for extending classes to implement. Before being thrown to you, a PEAR_Exception object can be initialized with another Exception or with an array of Exception objects. In this way, PEAR packages can wrap Exception objects. You can get at wrapped exceptions by calling PEAR::getCause(). This will either return a wrapped Exception object, an array if there is more than one, or null if none are found. PEAR_Exception also uses the Observer pattern, allowing you to register callback functions or methods that will be called whenever an exception is thrown. First, let’s create some error conditions: class MyPearException extends PEAR_Exception { } class MyFeedThing { function acquire( $source ) { try { $myfeed = @new XML_Feed_Parser( $source ); return $myfeed; } catch ( XML_Feed_Parser_Exception $e ) { throw new MyPearException( "feed acquisition failed", $e ); } } } I extend PEAR_Exception and create a simple class that wraps XML_Feed_Parser. If the XML_Feed_Parser constructor throws an exception, I catch it and pass it to the constructor of MyPearException, which I then rethrow. This trick allows me to raise my own error while bundling the root cause. Here is a client class and a couple of lines of code to invoke it: Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 333 class MyFeedClient { function __construct() { PEAR_Exception::addObserver( array( $this, "notifyError") ); } function process() { try { $feedt = new MyFeedThing(); $parser = $feedt->acquire('wrong.xml'); } catch ( Exception $e ) { print "an error occurred. See log for details\n"; } } function notifyError( PEAR_Exception $e ) { print get_class( $e ).":"; print $e->getMessage()."\n"; $cause = $e->getCause(); if ( is_object( $cause ) ) { print "[cause] ".get_class( $cause ).":"; print $cause->getMessage()."\n"; } else if ( is_array( $cause ) ) { foreach( $cause as $sub_e ) { print "[cause] ".get_class( $sub_e ).":"; print $sub_e->getMessage()."\n"; } } print "----------------------\n"; } } $client = new MyFeedClient(); $client->process(); All the usual caveats about sample code apply here, of course—especially since this particular example is designed to fail. First of all, notice the constructor. PEAR_Exception::addObserver() is a static method that accepts a callback, either a function name or an array containing an object reference and a method name. The method or function will be invoked every time a PEAR_Exception is thrown. This trick allows us to design MyFeedClient so that it logs all exceptions. The process() method passes a nonexistent file to MyFeedThing::acquire(), which passes it on to the XML_Feed_Parser constructor, thereby guaranteeing an error. We catch the inevitable exception and print a simple message. notifyError() is the callback method I referenced in the MyFeedClient constructor. Notice that it expects a PEAR_Exception object. In this case, I simply query the object and print out error information, although in a real-world situation, I would probably send this data to a log. Notice the call to PEAR_Exception::getCause(). Because this could return an array or a single Exception object, I handle both cases. If I run this toy code, this is what I get: Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 334 XML_Feed_Parser_Exception:Invalid input: this is not valid XML ---------------------- MyPearException:feed acquisition failed [cause] XML_Feed_Parser_Exception:Invalid input: this is not valid XML ---------------------- an error occurred. See log for details Our logger method is invoked for both the exceptions thrown by this sample (the first by XML_Feed_Parser, the second by MyFeedThing). The XML_Feed_Parser_Exception object makes a second appearance in the log output because we added it to the MyPearException object as a cause. Creating Your Own PEAR Package Packages from the PEAR repository are well documented and designed to be easy to use. How easy are they to create, though, and how do you go about creating your own? In this section, we will look at the anatomy of a PEAR package. package.xml The package.xml file is the heart of any PEAR package. It provides information about a package, determines where and how its participants should be installed, and defines its dependencies. Whether it operates on a URL, the local file system, or a tarred and gzipped archive, the PEAR installer needs the package.xml file to acquire its instructions. No matter how well designed and structured your package is, if you omit the build file, the install will fail. Here’s what happens if you attempt to install an archive that does not contain package.xml: $ pear install baddialekt.tgz could not extract the package.xml file from "baddialekt.tgz" Cannot initialize 'baddialekt.tgz', invalid or missing package file Package "baddialekt.tgz" is not valid install failed The PEAR installer first unpacks our archive to the temporary directory and then looks for package.xml. Here, it falls at the first hurdle. So if package.xml is so important, what does it consist of? Package Elements The package file must begin with an XML declaration. All elements are then enclosed by the root package element: <?xml version="1.0" encoding="UTF-8"?> <package packagerversion="1.4.11" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 335 <!-- additional elements here --> </package> This example would fail with an error. The PEAR installer requires a number of elements to work with. To start with, we must provide overview information: <name>Dialekt</name> <channel>pear.example.com</channel> <summary>A package for translating text and web pages into silly tones of voice</summary> <description>Be the envy of your friends with this hilarious dialect translator. Easy to extend and altogether delightful. </description> <!-- additional elements here --> These new elements should be pretty self-explanatory. The name element defines the handle by which the user will refer to the package. The summary element contains a one-line overview of the package, and description provides a little more detail. All these elements are compulsory with the exception of channel. If you are not intending to add your package to a channel you can use the uri element instead of channel, and in the same part of the file This should contain a URI that points to your package file: <uri>http://www.example.com/projects/Dialekt-1.2.1</uri> The file name should not include an extension, even though the package file itself will likely end with a .tgz extension. Next, you should provide information about the team behind your package. You should include at least one lead element: <lead> <name>Matt Zandstra</name> <user>mattz</user> <email>matt@example.com</email> <active>yes</active> </lead> After this, you can define other projects participants in a similar way. Instead of lead, though, you can use developer, contributor, or helper elements. These are designations recognized by the PEAR community, but they should adequately cover most non-PEAR projects too. The user element refers to the contributor’s user name with PEAR. Most teams use similar handles to allow users to log in to Subversion, a development server, or both. Before you get to the files in your project, there are a few more details you must provide: <date>2010-02-13</date> <time>18:01:44</time> <version> <release>1.2.1</release> <api>1.2.1</api> </version> <stability> <release>beta</release> <api>beta</api> </stability> <license uri="http://www.php.net/license">PHP License</license> Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 336 <notes>initial work </notes> Although this is mostly self-explanatory, it’s worth pointing out a couple of features. Of the elements inside version, release is the one that really counts as far as your package is concerned. The release element is used by PEAR in dependency calculations. If another system claims to require Dialekt 1.0.0, and the installing user only has version 0.2.1 on her system, PEAR will halt its installation or attempt to fetch a later version, depending on the mode in which it was run. The api element, on the other hand, is there so that you can keep track of changes in your code’s interface which may affect compatibility. The stability element is similarly split between release and api. The value can be one of snapshot, devel, alpha, beta, or stable; you should choose the one that best describes your project. If you are releasing your package according to specific license terms (such as GNU’s GPL license, for example) you should add this information to the license element. Unlike summary and description, the notes element will accept line breaks in the contents you add. The contents Element Now, we’re finally ready to talk about the files and directories in the package. The contents element defines the files that will be included in the package archive (sometimes called a tarball, because it’s archived with the tar and Gzip tools). You can describe the structure of your archive by combining dir and file elements. Here’s a simplified example: <contents> <dir name="/"> <dir name="data"> <file name="alig.txt" role="data" /> <file name="dalek.txt" role="data" /> </dir> <!-- /data --> <dir name="Dialekt"> <file name="AliG.php" role="php" /> <file name="Dalek.php" role="php" /> </dir> </contents> Every file in a PEAR package has a role. Every role is associated with a default (configurable) location. Table 15–2 describes the common roles. Table 15–2. Some Common PEAR File Roles Role Description PEAR Config Name Example Location php PHP file php_dir /usr/local/lib/php test Unit test file test_dir /usr/local/lib/php/test/<package> script Command line script bin_dir /usr/local/bin data Resource file data_dir /usr/local/lib/php/data/<package> doc Documentation file doc_dir /usr/local/lib/php/doc/<package> Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 337 When installation takes place, files of role doc, data, and test are not dropped directly into their respective directories. Instead, a subdirectory named after the package is created in the test_dir and data_dir directories, and files are installed into this. In a PEAR project, everything must have a role, and every role has its place. If you do not have the correct privileges to work with the default role locations, you can set your own locations using the pear command line tool: $ pear config-set php_dir ~/php/lib/ $ pear config-set data_dir ~/php/lib/data/ $ pear config-set bin_dir ~/php/bin/ $ pear config-set doc_dir ~/php/lib/doc/ $ pear config-set test_dir ~/php/lib/test/ ■ Note Pyrus uses set rather than config-set for the same purpose. Now, PEAR will use your directories rather than those described in Table 15–2. Remember that if you do this, you should add the lib directory to your include path: either in the php.ini file, an .htaccess file, or using the ini_set() function in your scripts. You should also ensure that the bin directory is in your shell’s path so that command line commands can be found. My example revolves around a fictitious package called Dialekt. Here is the package’s directory and file structure: ./package.xml ./data ./data/dalek.txt ./data/alig.txt ./script ./script/dialekt.sh ./script/dialekt.bat ./cli-dialekt.php ./Dialekt.php ./Dialekt ./Dialekt/AliG.php ./Dialekt/Dalek.php As you can see, I have mirrored some of the standard PEAR roles in my data structure. So I include data and script directories. The top-level directory contains two PHP files. These should be installed in the PEAR directory (/usr/local/php/lib by default). Dialekt.php is designed to be the first port of call for client code. The user should be able to include Dialekt with require_once("Dialekt.php"); Additional PHP files (Dalek.php and AliG.php) are stored in a Dialekt directory that will be added to the PEAR directory (these are responsible for the detailed process of translating web pages and text files into oh-so-funny versions of themselves). Dialekt.php will include these on behalf of client code. So that the installed Dialekt package will be callable from the command line, we have included a shell script that will be moved to PEAR’s script directory. Dialekt uses configuration information stored in text files. These will be installed in PEAR’s data directory. Here's the full contents tag: Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS 338 <contents> <dir name="/"> <dir name="data"> <file name="alig.txt" role="data" /> <file name="dalek.txt" role="data" /> </dir> <!-- /data --> <dir name="Dialekt"> <file name="AliG.php" role="php" /> <file name="Dalek.php" role="php" /> </dir> <!-- /Dialekt --> <dir name="script"> <file name="dialekt.bat" role="script"> <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" /> <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" /> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> </file> <file name="dialekt.sh" role="script"> <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" /> <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" /> <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" /> </file> </dir> <!-- /script --> <file name="cli-dialekt.php" role="php" /> <file name="Dialekt.php" role="php"> <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" /> </file> </dir> <!-- / --> </contents> I have included a new element in this fragment. The tasks:replace element causes the PEAR installer to search the file for the trigger string given in the from attribute, replacing it with the pear- config setting in the to attribute. So the Dialekt.php file, for example, might start out looking like this: <?php /* * Use this from PHP scripts, for a CLI implementation use * @bin_dir@/dialekt */ class Dialekt { const DIALEKT_ALIG=1; const DIALEKT_DALEK=2; // . } After installation, the same class comment should look something like this: /* * Use this from PHP scripts, for a CLI implementation use * /home/mattz/php/bin/dialekt */ Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... if PHP was compiled with zlib support pear install PhpDocumentor-1.4.3.tgz Alternatively, you can uncompress the archive and work with phpDocumentor directly from the distribution directory The command line interface is handled by the file phpdoc, and you need to have the library directory phpDocumentor in your include path tar -xvzf PhpDocumentor-1.4.3.tgz cd PhpDocumentor-1.4.3 chmod 755 phpdoc /phpdoc... DOCUMENTATION WITH PHPDOCUMENTOR So in the following DocBlock comment, I document the CommandContext object and emphasize the fact that it is commonly used in the Command::execute() method: /** * Encapsulates data for passing to, from and between Commands * Commands require disparate data according to context The * CommandContext object is passed to the Command::execute() * method and contains data... classes and files in the project are listed in the left-hand frame Both the project name and the package name are incorporated into the documentation The class names are all hyperlinks In Figure 16–2, you can see some of the documentation for the Command class I created in Chapter 11 phpDocumentor is smart enough to recognize that Command is an abstract class, and that it is extended by FeedbackCommand and. .. packages in the project phpDocumentor can be run as a command line tool or through a slick web GUI I will concentrate on the command line, because it’s easy then to embed documentation updates into build tools or shell scripts The command to invoke phpDocumentor is phpdoc You will need to run the command with a number of arguments in order to generate documentation Here’s an example: phpdoc -d megaquiz/... • The phpDocumentor application: Installing phpDocumentor and running it from the command line • Documentation syntax: The DocBlock comment and documentation tags • Documenting your code: Using DocBlock comments to provide information about classes, properties, and methods • Creating links in documentation: Linking to web sites and to other documentation elements Why Document? Programmers love and loathe... WITH PHPDOCUMENTOR Documenting Classes Let’s add some more tags and text that are useful in class- or file-level DocBlocks I should identify the class, explain its uses, and add authorship and copyright information Here is the Command class in its entirety: /** * Defines core functionality for commands * Command classes perform specific tasks in a system via * the execute() method * * @package command... can move or upload the package to my channel directory Then, before I run the command to make the release, I must first check a setting in the php. ini file If you don’t know where that is, by the way, you can run php ini from the command-line and PHP will tell you Once I’ve looked up its location I can open up php. ini and look for a line like this: phar.readonly = Off If the line is not there, or if... info Once again, the URL is the target, and the description that follows it is the clickable text You may want to make a reciprocal link Command uses CommandContext objects, so I can create a link from Command::execute() to the CommandContext class and a reciprocal link in the opposite direction I could, of course, do this with two @link or @see tags @uses handles it all with a single tag, however:... pear.appulsus.com A package for translating text and web pages into silly tones of voice 344 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS and regenerate the PEAR package Analyzing Dialekt/AliG .php Analyzing Dialekt/Dalek .php Analyzing cli-dialekt .php Analyzing Dialekt .php Package Dialekt-1.2.1.tgz done As before,... watermark 339 CHAPTER 15 ■ AN INTRODUCTION TO PEAR AND PYRUS Table 15–3 package.xml Dependency Types Element Description php The PHP application package A PEAR package extension A PHP extension (a capability compiled into PHP such as zlib or GD) arch Operating system and processor architecture os An operating system Up until now I have specified mandatory dependencies In fact, after requires, you can . Example Location php PHP file php_ dir /usr/local/lib /php test Unit test file test_dir /usr/local/lib /php/ test/<package> script Command line script. PEAR AND PYRUS 345 and regenerate the PEAR package Analyzing Dialekt/AliG .php Analyzing Dialekt/Dalek .php Analyzing cli-dialekt .php Analyzing Dialekt.php

Ngày đăng: 14/12/2013, 17:15

Từ khóa liên quan

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

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

Tài liệu liên quan