While working on converting old and flaky code to our current framework, making it more object orientated, with less duplication and easier to understand and use I thought I'd cover a few things which to me make classes, methods and interfaces "Programmer Friendly".
1. Doc Comments
You might be in a hurry, but documentation comments can speed up development to an order of magnitude. With most advanced PHP editors such as PDT supporting JavaDoc style comments and subsequently type hinting from them for auto-completion, taking a few extra seconds to add these annotations is well worth it.
interface BelongsToCompany {
/**
* @return Company
*/
public function company();
}
2. Flexible Parameters
Always require the minimum amount of data from the programmer to run the function. For example if you only require the `Company` primary key to run some queries, the only require the primary key as a parameter.
However it can be useful to convert known input types into what you want, allowing the programmer to pass a Company object or a User object (which belongs to a Company) as the parameters and to get the company ID from that.
class Something {
/**
* @param integer|Company $company_id
*/
public function staffCount( $company_id )
{
if( $company_id instanceof BelongsToCompany ) {
$company_id = $company_id->company();
}
if( $company instanceof Company ) {
$company_id = $company_id->id;
}
}
}
3. Use method Entry contracts
Wherever you implement an interface method, and generally any method, add input contracts using asserts() for more robust code and faster less obscure error feedback to other programmers. A fine balance is needed between allowing flexible parameters usually setup above the entry contract, and ensuring that the main body of code only runs with the correct types of parameters.
I'm including this under the topic of flexibility because these provide safeguards to let the programmer know when then underlying function cannot handle or automatically convert the parameters.
Some situations would be:
- $parameter is a number
- $parameter is an instance of SomeClass or SomeInterface
- If $param1 is passed, ensure $param2 is there too
- Ensure $parameter is not empty
However, avoid using `is_int` or `is_bool`, or other underlying functions which query the underlying ZVAL structure in entry contracts; instead use the `ctype_*` functions or type-casts first, then check that the values are within expected ranges.
/**
* @param integer $p1
* @param boolean $p2
* @param string $p3
*/
function testContract( $p1, $p2, $p3 ) {
$p1 = (int)$p1;
assert( $p1 > 0 );
// Boolean is implicitly within range
$p2 = (boolean)$p2;
assert( !empty($p3) );
assert( strlen($p3) < 100 );
}
4. Be Stateful and Refactor
One of my major gripes are groups of 'stateless' static methods which all accept a common parameter. These should be flagged early on for re-factoring as they make your classes brittle and inhibit changes in future. Common places where these pop up are when procedural code has been hastily converted into classes.
class Company {
public static function getUsers( $company_id ) {}
public static function getPeople( $company_id ) {}
public static function getCustomers( $company_id ) {}
}
One of the problem factors here is that the programmer is required to carry the objects state around wherever it's being used and implicitly pass it when calling the methods. A particularly bad example would be:
$users = Company::getUsers( $this->company->id );
In an effort to breathe as much life into an old bit of software he was having to update, Harry Roberts worked up a list of things that he sees can make things a bit more “programmer friendly” when it comes to using classes...
Is there a mistake in the second example (Flexible Parameters)? I think $company should be $company_id ...