• No se han encontrado resultados

Teorías Relacionadas al tema

In document FACULTAD DE INGENIERÍA (página 39-67)

I. INTRODUCCIÓN

1.3. Teorías Relacionadas al tema

else {

print Type assignment error\n; debug_print_backtrace();

} } } }

When an assignment occurs, the property being assigned to is looked up in

self::$types, and its validation function is run. If you match types correctly, every-thing works like a charm, as you see if you run the following code:

$obj = new Typed;

$obj->name = George;

$obj->counter = 1;

However, if you attempt to violate your typing constraints (by assigning an array to

$obj->name, which is specified of type is_string), you should get a fatal error.

Executing this code:

$obj = new Typed;

$obj->name = array(George);

generates the following error:

> php 20.php

Type assignment error

#0 typed->_ _set(name, Array ([0] => George)) called at [(null):3]

#1 typed->unknown(name, Array ([0] => George)) called at [/Users/george/

Advanced PHP/examples/chapter-2/20.php:28]

SPL and Interators

In both of the preceding examples, you created objects that you wanted to behave like arrays. For the most part, you succeeded, but you still have to treat them as objects for access. For example, this works:

$value = $obj->name;

But this generates a runtime error:

$value = $obj[name];

Equally frustrating is that you cannot use the normal array iteration methods with them.

This also generates a runtime error:

foreach($obj as $k => $v) {}

To enable these syntaxes to work with certain objects, Marcus Boerger wrote the Standard PHP Library (SPL) extension for PHP5. SPL supplies a group of interfaces, and

it hooks into the Zend Engine, which runs PHP to allow iterator and array accessor syn-taxes to work with classes that implement those interfaces.

The interface that SPL defines to handle array-style accesses is represented by the fol-lowing code:

interface ArrayAccess { function offsetExists($key);

function offsetGet($key);

function offsetSet($key, $value);

function offsetUnset($key);

}

Of course, because it is defined inside the C code, you will not actually see this defini-tion, but translated to PHP, it would appear as such.

If you want to do away with the OO interface to Tied completely and make its access operations look like an arrays, you can replace its _ _get() and _ _set() operations as follows:

function offsetGet($name) {

$data = dba_fetch($name, $this->dbm);

if($data) {

return unserialize($data);

} else {

return false;

} }

function offsetExists($name) {

return dba_exists($name, $this->dbm);

}

function offsetSet($name, $value) {

return dba_replace($name, serialize($value), $this->dbm);

}

function offsetUnset($name) {

return dba_delete($name, $this->dbm);

}

Now, the following no longer works because you removed the overloaded accessors:

$obj->name = George; // does not work

But you can access it like this:

$obj[name] = George;

65 Overloading

If you want your objects to behave like arrays when passed into built-in array functions (e.g., array map( )) you can implement the Iteratorand IteratorAggregate interfaces, with the resultant iterator implementing the necessary interfaces to provide support for being called in functions which take arrays as parameters. Here’s an example:

interface IteratorAggregate { function getIterator();

}

interface Iterator { function rewind();

function hasMore();

function key();

function current();

function next();

}

In this case, a class stub would look like this:

class KlassIterator implemnts Iterator { /* ... */

}

class Klass implements IteratorAggregate { function getIterator() {

return new KlassIterator($this);

}

/* ... */

}

The following example allows the object to be used not only in foreach()loops, but in

for()loop as well:

$obj = new Klass;

for($iter = $obj->getIterator(); $iter->hasMore(); $iter = $iter->next()) {

// work with $iter->current() }

In the database abstraction you wrote, you could modify DB_Result to be an iterator.

Here is a modification of DB_Result that changes it’s API to implement Iterator:

class DB_Result { protected $stmt;

protected $result = array();

protected $rowIndex = 0;

protected $currIndex = 0;

protected $max = 0;

protected $done = false;

function _ _construct(DB_Statement $stmt) {

if($this->done && $this->max == $this->currIndex) { return false; if($this->done && ) {

return false;

}

$offset = $this->currIndex + 1;

if(!$this->result[$offset]) {

67 Overloading

Additionally, you need to modify MysqlStatement to be an IteratorAggregate, so that it can be passed into foreach()and other array-handling functions. Modifying

MysqlStatementonly requires adding a single function, as follows:

class MysqlStatement implements IteratorAggregate { function getIterator() {

return new MysqlResultIterator($this);

} }

If you don’t want to create a separate class to be a class’s Iterator, but still want the fine-grain control that the interface provides, you can of course have a single class imple-ment both the IteratorAggregateandIteratorinterfaces.

For convenience, you can combine the Iterator and Array Access interfaces to create objects that behave identically to arrays both in internal and user-space functions.This is ideal for classes like Tiedthat aimed to pose as arrays. Here is a modification of the Tied class that implements both interfaces:

class Tied implements ArrayAccess, Iterator { private $dbm;

private $dbmFile;

private $currentKey;

function _ _construct($file = false) {

$this->dbmFile = $file;

$this->dbm = dba_popen($this->dbmFile, w, ndbm);

}

$data = dba_fetch($name, $this->dbm);

if($data) {

function offsetUnset($name) {

return dba_delete($name, $this->dbm);

}

return dba_replace($name, serialize($value), $this->dbm);

}

function rewind() {

$this->current = dba_firstkey($this->dbm);

}

function current() {

$key = $this->currentKey;

if($key !== false) {

return $this->_ _get($key);

} }

function next() {

$this->current = dba_nextkey($this->dbm);

}

function has_More() {

return ($this->currentKey === false)?false:true;

}

function key() {

return $this->currentKey;

} }

To add the iteration operations necessary to implement Iterator,Tieduses dba_firstkey()to rewind its position in its internal DBM file, and it uses dba_

nextkey()to iterate through the DBM file.

With the following changes, you can now loop over a Tiedobject as you would a normal associative array:

$obj = new Tied(/tmp/tied.dbm);

$obj->foo = Foo;

$obj->bar = Bar;

$obj->barbara = Barbara; foreach($a as $k => $v) {

print $k => $v\n; }

Running this yields the following:

foo => Foo counter => 2 bar => Bar

barbara => Barbara

Where did that countercome from? Remember, this is a persistent hash, so counter still remains from when you last used this DBM file.

69 Overloading

_ _call()

PHP also supports method overloading through the _ _call()callback.This means that if you invoke a method of an object and that method does not exist,_ _call() will be called instead. A trivial use of this functionality is in protecting against undefined methods.The following example implements a _ _call()hook for a class that simply prints the name of the method you tried to invoke, as well as all the arguments passed to the class:

class Test {

public function _ _call($funcname, $args) {

print Undefined method $funcname called with vars:\n; print_r($args);

} }

If you try to execute a nonexistent method, like this:

$obj = new Test;

$obj->hello(george);

you will get the following output:

Undefined method hello called with vars:

Array (

[0] => george )

_ _call()handlers are extremely useful in remote procedure calls (RPCs), where the exact methods supported by the remote server are not likely to know when you imple-ment your client class. RPC methods are covered in depth in Chapter 16, “RPC:

Interacting with Remote Services.”To demonstrate their usage here briefly, you can put together an OO interface to Cisco routers.Traditionally, you log in to a Cisco router over Telnet and use the command-line interface to configure and maintain the router.

Cisco routers run their own proprietary operating system, IOS. Different versions of that operating system support different feature sets and thus different command syntaxes.

Instead of programming a complete interface for each version of IOS, you can use _ _call()to automatically handle command dispatching.

Because the router must be accessed via Telnet, you can extend PEAR’s Net_Telnet class to provide that layer of access. Because the Telnet details are handled by the parent class, you only need two real functions in the class.The first,login(), handles the spe-cial case of login.login()looks for the password prompt and sends your login creden-tials when it sees the password prompt.

TEAM FLY

PEAR

PHP Extension and Application Repository (PEAR) is a project that is loosely associated with the PHP group.

Its goal is to provide a collection of high-quality, OO, reusable base components for developing applications with PHP. Throughout this book, I use a number of PEAR classes. In both this book and my own program-ming practice, I often prefer to build my own components. Especially in performance-critical applications, it is often easiest to design a solution that fits your exact needs and is not overburdened by extra fluff.

However, it can sometimes be much easier to use an existing solution than to reinvent the wheel.

Since PHP 4.3, PHP has shipped with a PEAR installer, which can be executed from the command line as follows:

> pear

To see the full list of features in the PEAR installer you can simply type this:

> pear help

The main command of interest is pear install. In this particular case, you need the Net_Telnet class to run this example. To install this class, you just need to execute this:

> pear install Net_Telnet

You might need to execute this as root. To see a complete list of PEAR packages available, you can run this:

> pear list-all

or visit the PEAR Web site, at http://pear.php.net.

The second function you need in the Net_Telnetclass is the _ _call()handler.This is where you take care of a couple details:

n Many Cisco IOS commands are multiword commands. For example, the com-mand to show the routing table is show ip route.You might like to support this both as $router->show_ip_route()and as $router->show(ip route).To this end, you should replace any _in the method name with a space and concatenate the result with the rest of the arguments to make the command.

n If you call a command that is unimplemented, you should log an error.

(Alternatively, you could use die()or throw an exception. Chapter 3 covers good error-handling techniques in depth.)

Here is the implementation of Cisco_RPC; note how short it is, even though it supports the full IOS command set:

require_once Net/Telnet.php; class Cisco_RPC extends Net_Telnet {

protected $password;

function _ _construct($address, $password,$prompt=false) {

71 Overloading

parent::_ _construct($address);

$this->password = $password;

$this->prompt = $prompt;

}

function login() {

$response = $this->read_until(Password:);

$this->_write($this->password);

$response = $this->read_until($this->prompt>);

}

function _ _call($func, $var) {

$func = str_replace(_, “ “, $func);

$func .= “ “.implode(“ “, $var);

$this->_write($func);

$response = $this->read_until($this->prompt>);

if($response === false || strstr($response, %Unknown command)) { error_log(Cisco command $func unimplemented, E_USER_WARNING);

} else {

return $response;

} } }

You can use Cisco_RPCquite easily. Here is a script that logs in to a router at the IP address 10.0.0.1 and prints that router’s routing table:

$router = new Cisco_RPC(10.0.0.1, password);

$router->login();

print $router->show(ip route);

_ _autoload()

The final magic overloading operator we will talk about in this chapter is

_ _autoload()._ _autoload()provides a global callback to be executed when you try to instantiate a nonexistent class. If you have a packaging system where class names correspond to the files they are defined in, you can use _ _autoload()to do just-in-time inclusion of class libraries.

If a class you are trying to instantiate is undefined, your _ _autoload()function will be called, and the instantiation will be tried again. If the instantiation fails the sec-ond time, you will get the standard fatal error that results from a failed instantiation attempt.

If you use a packaging system such as PEAR, where the class Net_Telnetis defined in the file Net/Telnet.php, the following _ _autoload()function would include it on-the-fly:

function _ _autoload($classname) {

$filename = str_replace(_,/, $classname). .php; include_once $filename;

}

All you need to do is replace each _with/to translate the class name into a filename, append.php, and include that file.Then if you execute the following without having required any files, you will be successful, as long as there is a Net/Telnet.phpin your include path:

<?php

$telnet = new Net_Telnet;

? >

Further Reading

There are a great number of excellent books on OO programming techniques and design patterns.These are by far my two favorite design pattern books:

n Design Patterns (by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides).This is called the “Gang of Four” book, after its four authors.This is the ultimate classic on patterns.

n Patterns of Enterprise Application Architecture (by Martin Fowler). Fowler is an incredi-bly experienced fellow, and this book is an insightful and extremely practical approach to design patterns, particularly on the Web.

Neither of these books focuses on PHP, but if you’re willing to wade through C++, C#, and Python, they are well worth the effort.

3

In document FACULTAD DE INGENIERÍA (página 39-67)

Documento similar