at 2007-01-29
in Editorial
by friebe
(0 comments)
Today at work we had a presentation called "PHP5 features", where Alex and me presented what we consider the most appealing new possibilities.
Here's a wrap-up:
Method call chaining Method call chaining is how we call foo()->bar()->baz(). You may also know it as "dereferencing"; the exact terminology is unknown
Examples in the XP framework:
<?php $cm= ConnectionManager::getInstance(); $conn= $cm->getByHost('vod', 0); $conn= ConnectionManager::getInstance()->getByHost('vod', 0); $l= Logger::getInstance(); $cat= $l->getCategory(); $cat= Logger::getInstance()->getCategory(); ?>
Please note the following: The possibility to chain method calls will have an impact on design decisions made:
Exceptions With "real" exceptions supported by PHP5, we can optimize the following:
- No more "useless" rethrowing
In PHP4, we often put try/catch $e/throw $e blocks inside our sourcecode because of the limitation of our exception simulation. These are no longer necessary because they resemble the default behaviour:
<?php try { } catch (Throwable $e) { throw $e; } ?> ...is the same as:
<?php ?>
- No more try/catch/printStackTrace and exit boilerplate
If you're using xp::sapi('cli'), there is no need to write sourcecode which catches exceptions, prints them and exits, this is what happens automatically.
<?php try { $bean= Remote::forName($rsn)->lookup('xp/demo/MessageSender'); } catch (RemoteException $e) { $e->printStackTrace(); exit(1); } ?> ...is the same as:
<?php $bean= Remote::forName($rsn)->lookup('xp/demo/MessageSender'); ?> when using xp::sapi('cli').
- Advanced: Return inside catch
<?php try { return ConnectionManager::getInstance()->getByHost('vod', 0); } catch (ConnectionNotRegisteredException $e) { throw new HttpScriptletException( $e->getMessage(), HTTP_PAYMENT_REQUIRED ); } ?> This has the advantage of not having to watch our for the codepath in the case of catching an exception; plus the reader can see right ahead what you are trying to do, and can examine what you do for error handling later.
- Transaction template:
We used to use this as for database transactions:
<?php try { $conn= ConnectionManager::getByHost('XXX', 0); $tran= $conn->begin(new Transaction('update_it'));
} catch (SQLException $e) { $tran->rollback(); throw $e; } $tran->commit(); ?> This "template" has the problem that $tran may not be set, e.g. due to "begin transaction" failing. It is suggested to use the following in the catch-block:
<?php $tran && $tran->rollback(); ?> This will prevent fatal errors.
At the same time, the $tran->commit() can be moved inside the try/catch block (right above the catch), which has the benefit that an error during committing a transaction would also be handled.
The "self" keyword Self can be used like parent, only that it refers to the class it's been placed in rather than the parent. You all know this:
<?php class A { public function foo() { } }
class B extends A { public function foo() { parent::foo(); } } ?> With "self", you get around typing (and potentially mistyping) the classname:
<?php class ConnectionManager { public static function getInstance() { return self::$instance; } } ?> Also nice for equals()-methods:
<?php public function equals($cmp) { return ( $cmp instanceof self && ($this->getTime() === $cmp->getTime() ); } ?> Type hints At the moment, we don't make use of type hints much. Type hints are supported in PHP5 for method arguments, as follows:
<?php class Node extends Object { public function addChild(Node $n) { } } ?> ...and will raise an E_RECOVERABLE_ERROR ("PHP Catchable fatal error") when the type is mismatched (an instanceof check is used here, so enableDebug(Traceable $instance) will work for any object whose class implements the Traceable interface).
The type hint may either be the word "array" or any other string which will then reference a class by that name. Type hints for primitives are not supported.
Type hints do not accept NULL values per default, this can be accomplished by making the parameter optional:
<?php class Leaf extends Object { public function setParent(Leaf $x= NULL) { } } ?> Now the setParent() method can be called as follows:
<?php $leaf->setParent(new Leaf()); $leaf->setParent(NULL); $leaf->setParent(); ?> ...but not as:<?php $leaf->setParent(1); $leaf->setParent('Hello'); ?> Please note that subclasses may not change method signatures! Thus if we have the following:
<?php class Base { public function setDate(Date $d) { } }
class Child extends Base { public function setDate(SqlDate $d) { } } ?> ...this will work only as long as setDate() is not defined via an interface (in that case, a fatal error "Declaration of Child::setDate() must be compatible with ..." is raised). Don't rely on the fact that the checks may not become stricter in the future! Btw, in Java, setDate(SqlDate d) and setDate(Date d) are two different methods, so this is not applicable there. You'd get an error message though if you were using the @Override annotation!
One can get around type hints be ignoring the catchable fatal errors:
<?php set_error_handler(create_function('', ''), E_RECOVERABLE_ERROR); class X { public static function y(X $x) { var_dump($x); } } X::y(1); ?> Btw: RFC #0100 proposes to turn E_RECOVERABLE_ERRORs into lang.IllegalArgumentExceptions, in this case, the method will obviously not be run.
More... We also talked about the feature of calling PHP functions during XSL transformations, e.g.:
<xsl:value-of select="php:function('time')"/>...or <xsl:value-of select="php:function('ucfirst', ./@name)"/>
For details, see RFC #0104 (implemented since 5.1.1-release).
|
Subscribe
You can subscribe to the XP framework's news by using RSS syndication.
CategoriesNews General PHP5 Announcements RFCs Further reading Examples Editorial EASC Experiments Unittests Databases
RelatedFind related articles by a search for «Making».
|