QuicksearchOuter LandsBlogs I ReadSyndicateCategories |
Environmental improvementsFriday, April 2. 2010
At work, I write PHP either directly in vim on the Linux test server (when debugging), or using gVim on Windows to edit files in my private tree. Today, I made a couple improvements to the setup, one to each environment.
First: using the keywordprg on Windows gVim to look up PHP functions in the manual. I have Python 2.5 installed (batteries included!), so I whipped up this script to work as keywordprg: I saved it as C:\Users\sapphirecat\bin\phpsearch.py and then edited C:\Users\sapphirecat\_vimrc to set keywordprg to the Python script. Now the K command in normal mode brings me to the PHP manual page for whatever function the cursor was on.Second: preventing vim on the server from highlighting HTML and SQL inside PHP strings. This was happening on the server in spite of having let php_sql_query=0 and friends inside ~/.vimrc. It turns out that the default PHP syntax file checks for the existence of the variable, but not its value, so 0 is considered the same as 1. Adding lockvar php_sql_query php_htmlInStrings php_show_preg actually prevents all three of those from becoming defined, and now my PHP strings are really just strings.
Interfaces, Constructors, and Dependency InjectionFriday, November 13. 2009
Welcome to our second PHPTips column. This time, we'll be talking about the details of interfaces, abstract base classes and inheritance, constructors, dependency injection, and even a bit about type hinting.
I'm sure you know the basics of interfaces: With this interface defined, if you have a function that asks for an IProperty object, e.g. function copyAryInto(IProperty $obj, $ary), you can be certain that you can call $obj->set($key, $value). The main property of interfaces is that they guarantee certain method signatures are available on an object implementing the interface. This is not necessarily that the actual method has exactly the same parameter list, but that you can call any object implementing IProperty using only the arguments defined in the interface.That means: function set ($bucket, $data, $meta=null) is OK, because the set() method can be called with two arguments, just as in the interface. If the interface defined a third parameter with a default, then you would have to define the method as taking at least three parameters, with the last having a default value. The default doesn't actually have to match; there just has to be a default.Incidentally, the initial "I" naming convention for interfaces is optional, but it turns out to be very helpful, since classes and interfaces share the same namespace. By prepending the "I" to interfaces, you can eliminate the chance of trying to create both a class and an interface of the same name. It can also help code readability to be able to tell at a glance whether a name is an interface or a class, for instance when reading type hints. Getting back to inheritance: how do interfaces and inheritance interact? It's actually quite simple. If a class implements an interface, then all of its subclasses do, without having to include implements IProperty in their definition, or provide any methods themselves. They are free to override them if they so desire, but they can also use their parent's implementation.What about abstract classes? It turns out that abstract classes actually do not have to implement all of the methods of the interface. The unimplemented methods are effectively copied to the abstract class as abstract methods, and any concrete subclass of the abstract class is then required to implement the remaining methods. Reusing the IProperty from above, two classes could technically be defined like this: A noticeable weak point of PHP's interfaces as compared to Java's, if you like strong typing, is that the PHP interface can't define a return type for the method. This happens because PHP itself doesn't allow the declaration of return types.The other interesting feature of PHP interfaces, as compared to Java's, is the fact that PHP 5 has a common name for constructors. This means that, since bug 34019 was fixed, an interface can effectively define the interface that a constructor should have in any implementing class. However, although the capability is there, it's my current opinion that it probably shouldn't be used. That is, if you're defining an interface with __construct, you're probably doing it wrong. It's not useful for type hinting, because if the object exists already, you're not going to generate any calls to __construct. (And all the other magic methods have a fixed, predefined signature, that would be pointless to include in an interface.) And if you're choosing some random object from a hierarchy to construct in the depths of your code, you could probably benefit from dependency injection. Dependency injection is, in a single sentence, the practice of demanding objects your class needs from the outside world, instead of creating them directly. Instead of reading Config::$DB_HOST and friends to pass into new PDO(), or calling DatabaseMaster::getDB(), the DI style is to take the database handle in as an argument to your class: function __construct($db). If you're big on data adapters, it can be type-hinted as IDatabase, or you can leave it un-hinted and fly by the seat of your pants. With interfaces, though, I prefer to type-hint, so that if I pass in the wrong object, the error message can be very specific, and I know exactly what methods are expected there by reading the interface. I don't have to reverse-engineer it by reading the method body around an undefined method call.Another problem with trying to put __construct in an interface is that two different interfaces, which each define a method of the same name, cannot both be implemented in the same class. The last thing I would say is that interfaces and inheritance are two different things. Any class that wants to pass the type-hint of another class must be a subclass of that class. If there are two unrelated classes you want to pass the type-hint for, it can't be done, since PHP doesn't offer multiple inheritance (or multiple headaches). Interfaces don't suffer from this restriction; any class can implement any number of interfaces, and still be free to inherit from a parent class. Therefore, inheritance should be used when classes have a definite relationship, and interfaces should be preferred when the methods could be applied to any object, such as with IProperty. A database connection could have some properties set on it, and so could an XmlTemplate, but neither is a kind of the other. To steal some terminology from other languages, interfaces are similar to protocols or roles. One object may know how to speak many protocols, or perform several different roles, even if it fills one specific place in the class hierarchy. isset() and empty()Tuesday, October 13. 2009
Welcome! In this very first PHPTips posting, I'll be adding to what Brandon Savage said in his post on using isset() and empty() to avoid notices (E_NOTICE messages) from PHP. Read that one first, because I'm not going to repeat it.
Why is it important to avoid notices? For me, the killer reason is error logging. If the log is filled with "Undefined variable $left", then "Undefined variable $letf" doesn't really stand out. It's also helpful to know immediately, when my script crashes because it couldn't call a method on a non-object, whether that's because I mistyped the object's variable name, or the variable really isn't an object. Moving on, I'd like to add to what Brandon said about isset() and empty() being language constructs, not real functions. That's hugely important, because it is the foundation for the rest of the points I'd like to make about them here. The first is that language constructs can't be invoked with call_user_func or variable functions. Also, these language operators cannot be used as the name of any function or method in a class. (Although that may change in the future, it definitely applies to the 5.2.x series that's currently enjoying wide usage.) It's actually necessary for isset() and empty() to be language operators to do their work. If isset() were a normal function, then the argument would be evaluated prior to calling isset, and trigger the notice. isset() and empty(), under the hood, both generate special code that accesses their argument in write context instead of the usual read context. This never generates a notice, but the PHP engine does let isset() know whether the variable existed, so it can pass that knowledge back to your script. What happens if you pass something that can't be written to into isset(), as in isset(ini_get('magic_quotes_gpc'))? You get the error, "Fatal error: Can't use function return value in write context in ...". This is the exact same error that happens with ini_get('magic_quotes_gpc') = 1 because the underlying mechanism of accessing the "variable", the function return value in this case, is the same.I hope that sheds some more light on isset(). Brandon's rules remain unmodified: for testing for "set to a false value", use empty(); for testing for "not null", use isset().
(Page 1 of 1, totaling 3 entries)
Originally based on the 'Coffee Cup' theme by David Cummins, then heavily modified right here at sapphirepaw.org. |