RSS
 

Posts Tagged ‘PHP’

Return null vs return void

05 Oct

The other day I had a discussion about that, and why important open source frameworks like CakePHP use void in @return doc tags when void and null are code-wise totally identical.
So here my reasoning why it is a good idea to make a distinction:

Help the developer, not the machine

We already discovered that it doesn’t help the machine to make the difference.
So if anything, it would help the developer. So does it? Yes, it does (as with all coding standards, these things are there to make it easier for the developer and avoid human error).
In fact, most modern IDEs would light up the function like a Xmas tree if you tried to use a return void function value.

The following would be marked yellow (warning) right away:

/**
 * @return void
 */
public function doSth() {
}
/**
 * @return void
 */
public function badCode() {
    return $this->doSth(); // This would be marked yellow
}

So it helps the developer to not try to use return values of those methods.
This would not happen without the explicit @return void annotation.

See the following screenshot of what PHPStorm, for example, can now help us with:

return-null-vs-return-void

And also the other way around:

enforce-return

Bottom line:
void as pseudo type is not used to state that we return nothing/null (which is still true from a value point of view), but it is used to state that we should not expect anything (not trying to use it’s return value in any way).

Other reasons maybe

Be explicit

If you just see the following code, you might not know immediately, if someone just forgot the return type statement. If it was there from the start you would automatically know.

/**
 * @param string $param Param
 */
public function doSth($param) {
    // very long code block...
}

Be consistent

Some methods end up to not have any doc block, just because the doc block would have the return void part:

/**
 * @param string $param Param
 */
public function doSth($param) {
}
public function doSthElse() {
}
/**
 * @param string $param Param again
 */
public function doSthAgain($param) {
}

Especially if you don’t use descriptions this is quite inconsistent.

Part of PSR-5 and PHP RFC

It is also part of the upcoming PSR-5 standard.
To point out that is not just fiction 😉
Even though they state it to be optional (omitting that type altogether would also be valid, of course).

As long term goal then, once PHP RFC void_return_type is implemented, we can easily use a script to adjust the code.
Without being explicit this will not work out. So better use the best practice approach now already and save yourself tons of work in the long run.

Automated checking

It will be easier to automate and verify this via code sniffer or other tools when explicitly set, whereas the omitting does not tell if it was on purpose or just forgotten.
Especially since it forces to code more return type aware from the beginning (see the following part on potentially hidden issues around mixed return types).

Implications to consider

This means you should also use it consistently through-out the code base. Here a few things to keep in your mind.

Whenever you return multiple types (mixed), do NOT use void, use null instead.
The reason is simple:
You are returning an object or not (null), but you are using/checking the value of it afterwards, so returning void would be a lie (or trying to check on a void return result would be "pointless").

/**
 * @param Object $object The object.
 * @return Object|null
 */
public function doSthAndGiveMeSth(Object $object) {
	if ($object->isNotValid(()) {
	    return null;
	}
	// ...
}
/**
 * @param Object $object The object.
 * @return void
 */
public function doSthWithIt(Object $object) {
    $returnValue = $this->doSth($object);
    // ...
}

This is also true for returning early inside those methods/functions.

Also always explicitly return null at the end of a "non-void" method, if nothing else is being returned.

For returning early in void methods, use the following:

/**
 * @param string $param Param
 * @return void
 */
public function doSth($param) {
	if ($param === null) {
	    return;
	}
	// ...
}

An explicit return; statement at the end of those methods is never necessary (= should not be used therefore) and implies void when omitted.

Do not use return statements for constructor __construct() and destructor __destruct(), as both per definition cannot return anything.
This is the only exception made.

Adjusting your IDE

You can adjust IDEs usually to automatically add @return void as default.

For PHPStorm go to Settings -> Editor -> File and Code Templates -> Includes (tab) -> PHP Function Doc Comment.
Replace the template with:

/**
#if (${PARAM_DOC} != "") ${PARAM_DOC}
    #if (${TYPE_HINT} != "void") *
    #end
#end
${THROWS_DOC}
 * @return ${TYPE_HINT}
 */

Other implications

Exceptions

Some frameworks use special methods that can return an object, or nothing, e.g. controller methods as actions.
In that case it can be convenient to not have to return explicit nulls all the time at the end of each action.
Maybe, in some rare use cases one could use null|void (and then Response|null|void) as a way to allow omitting the explicit returns but to show that it would actually allow checking for the returned result afterwards. The IDE won’t complain then, either.
That should definitely stay the exception to the rule then, of course.

Building fluid interfaces

Once you use use a lot of @return void you end up using their methods appropriately – which implies that you don’t use the return value at all (not permitted).
That concludes that you could, basically without breaking the API, start returning the object itself ($this as in @return self). This transforms the class into fluid interfaces which allow method chaining then.
So look for those return types in your classes and think about where those could be part of a fluent interface pattern. Might not always be a good idea, but could very well be in some places.

Further reading

Take a look at php-void-much-ado-about-nothing.

It is also part of PSR-2-R Additions.

Update 2015-11

So the void RFC has been approved. Will be part of PHP 7.1 then 🙂

 
No Comments

Posted in PHP

 

UTF8 can be tricky – especially with PHP

15 Aug

Everybody uses (or should!) UTF8 these days. An easy and fully supporting PHP version I did not come across yet, though.
It seems there is sometimes more to it. This article is supposed to guideline the basic setup of a CakePHP app using UTF8 and will go beyond that to the really tricky parts regarding the de facto standard encoding these days.

Note: this post is really long overdue and was in my draft folder for 2+ years. So here it is, quickly published before it got even more dusty^^
And dusty sure is the right word with (hopefully) no one using ANSI/ISO-8859-1 anymore these days.

UTF8 and PHP

Use the mb_ functions if you know that you real with strings than can contain UTF8 chars. So if you want to count the length of such a string:

$length = mb_strlen($string);

If you are simply manipulating strings, you do not always have to use those slower and UTF8-aware fnctions, though. But in doubt always do so.

UTF8 and preg_match()

Now this is a tricky one – especially if you don’t want to recompile PHP with the PCRE UTF-8 flag enabled or if you don’t know about it at all. IMO that should be the default, but it usually isn’t it seems.

Most times, when dealing with UTF8 strings, the /u modifier and p{L} helps:

preg_match('/^\p{L}[\p{L} _.-]+$/u', $username, $matches)

In other cases you might have to add (*UTF8) in your pattern.

UTF8 and CakePHP

CakePHP setup

The main parts are handled in the book, especially in the getting-started section.
But the main part that sometimes people get wrong is that the APP encoding is "utf-8" while in the database.php its spelled utf8.

Make sure you save all files as "UTF8 without BOM" via your IDE as soon as they start to contain UTF8 chars. Failing to do so will cause output issues.
I usually try to avoid this and use Locale translation and mainly English chars in all files as much as possible.

Note: Before adding any UTF8 chars to files, those files are always ANSI (there is no way without the BOM to distinguish those two encoding formats as they are one and the same here). So no matter how often you try to save them as UTF8, they will always still be ANSI. In case you wondered why it falls back to it again in most IDEs.

Correcting PHP functions

Some PHP functionality has been wrapped in CakePHP to overcome deficiencies regarding Unicode.
String::wordWrap() for example replaces the faulty wordwrap() function.

I also added a few fixes to my Tools plugin as Utility/Utility class:

  • pregMatch(): Unicode aware replacement for preg_match()
  • pregMatchAll(): Unicode aware replacement for preg_match_all()
  • strSplit(): Unicode aware replacement for str_split()
  • pregMatchAll(): Unicode aware replacement for preg_match_all()

Probably more to come..

Proper validation

Make sure your validation is unicode aware – that’s probably one of the most made mistakes from mainly English speaking devs/people.
They maybe assume that it works to simply use strlen() or a [a-z] regex or alike – not taking into account that for example many normal first/last names contain a lot of special chars.
Validation here should never be too strict. Otherwise a lot of people will be very upset.

So in the above example we do NOT want to use

preg_match('/^\[a-z][a-z .-]+$/i', $firstName)

but something more like

preg_match('/^\p{L}[\p{L} .-]+$/u', $firstName)

to validate a first name.
IF we actually have to validate this further than a simple "not empty" check is a different topic (I don’t think so). But if you really must, PLEASE do not shut people out because their parents gave them non-English names 😉

A similar thing I had to fix in the core a while back, regarding domains/urls.
And this is CakePHP2.5 – the current master – so that topic sure is still quite current for some cases. More and more so with further internationalization.

Checklist for your CakePHP app

  • Ideally, use utf8_unicode_ci as collasion for your DB
  • Your layout should contain <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  • The apache/nginx should serve files as UTF8 via header Content-Type text/html; charset=UTF-8

Outview

Only in PHP7 (as PHP6 got skipped) there will be a more built-in approach then for UTF8. Until then (and maybe even then) we will have to fight quite a lot here for the next years.

There are even a few popular projects in GitHub around the UTF8 issues, e.g:

  • https://github.com/nicolas-grekas/Patchwork-UTF8
  • https://github.com/heartsentwined/php-utf8
  • https://github.com/voku/portable-utf8
  • https://github.com/gatsu/UTF8

Might be worth checking out.

Anything missing? Please let me know.

 
4 Comments

Posted in CakePHP

 

Interesting (CakePHP/PHP) links – 2013

16 Apr

PHP5.5

Try/Catch/Finally is a nice article about upcoming 5.5 features – and how to use them wisely.

Enhance your select form fields

Chosen is a beautiful addon for your select dropdowns, especially if you use them to filter to search on a large set of data and if you do not yet use any AJAX here. Just add some markup and JS and you got a quick and easy way to handle them.

There are also some CakePHP Plugins for the Chosen script available already.

SQL to Cake find statements

You can use this converter to form your SQL snippets into Cake find() calls. This is especially handy for beginners, but can also be useful to automate some migration scripts etc.

 

CakePHP Tips

22 Jan

All new CakePHP tips collected over the last few weeks.

Dispatcher execution order

Tested on Cake2.3RC:

  • APP/webroot/index.php
  • CORE/bootstrap.php
  • APP/Config/core.php
  • APP/Config/bootstrap.php
  • dispatchers defined in CORE bootstrap
  • APP/Config/routes.php
  • APP/Config/database.php
  • controller and all the rest

It is important to know that the dispatchers will be triggered before your routes are even loaded.
If you enabled a dispatcher like the CacheDispatcher, the last three elements might not even be triggered anymore (if the cached view file can be found) and the content might directly get sent at this point.

Resolve dispatcher conflicts

So if you implemented some routing for subdomains or other domains locked on the same application you need to make sure that the CacheDispatcher, for example, does not create a conflict. You can use the new Cache.viewPrefix (cake2.3) here in your bootstrap to not serve the wrong cached file here.

Callback execution order

Sometimes the execution order can be pretty important. At this point it is good to know what callback is triggered at what point – and the order is what you need/expect.

Note: Don’t trust what somebody tells you. Do it yourself. So instead of just believing my post here, execute the code yourself, if possible. You can have yourself some neat little callback execution tests for times where you need them.

Behavior/Model callbacks

// on save
Array
(
    [0] => TestBehavior::beforeValidate
    [1] => TestComment::beforeValidate
    // validate
    [2] => TestBehavior::afterValidate
    [3] => TestComment::afterValidate
    [4] => TestBehavior::beforeSave
    [5] => TestComment::beforeSave
    // save
    [6] => TestBehavior::afterSave
    [7] => TestComment::afterSave
)
// on find
Array
(
    [0] => TestBehavior::beforeFind
    [1] => TestComment::beforeFind
    // find
    [2] => TestBehavior::afterFind
    [3] => TestComment::afterFind
)
// on delete
Array
(
    [0] => TestBehavior::beforeDelete
    [1] => TestComment::beforeDelete
    // delete
    [2] => TestBehavior::afterDelete
    [3] => TestComment::afterDelete
)

Controller/Component callbacks

Array
(
    [0] => TestComponent::initialize
    [1] => TestController::beforeFilter
    [2] => TestComponent::startup
    [3] => TestController::test // action itself
    [4] => TestComponent::beforeRender
    [5] => TestController::beforeRender
    // rendering view
    [6] => TestComponent::shutdown
    [7] => TestController::afterFilter
    // output
)

This makes sense. The components first initialize themselves and might modify the controller’s "init" state prior to its dispatching of beforeFilter.
Then the components do their work before and after the action itself and at the end afterFilter is invoked prior to outputting the result.

When actually redirecting the following callbacks will not be executed anymore, though:

// in case of a redirect:
    [0] => TestController::redirect();
    [1] => TestComponent::beforeRedirect();
// redirect if not returned false, otherwise continue

Note: The redirect will only happen if the component’s callback does not return false here.

Helper callbacks

This is a little bit more tricky. The templating in cake uses a two-pass-rendering. First the view will be rendered and then it will be "injected" into the layout. So you cannot just echo debug output, you need to log the execution order if you want correct results here.

Using my test case for it, we get:

Array
(
    [0] => TestHelper::beforeRender
    [1] => TestHelper::beforeRenderFile
    // render view
    [2] => TestHelper::afterRenderFile
    [3] => TestHelper::afterRender
    [4] => TestHelper::beforeLayout
    [5] => TestHelper::beforeRenderFile
    // render layout and insert rendered view into "content" block
    [6] => TestHelper::afterRenderFile
    [7] => TestHelper::afterLayout

Interesting is, that beforeRenderFile and afterRenderFile are invoked for each file. If you include elements they will also be invoked for them. They are quite handy if you want to directly modify the rendered result in some way. This can also be a decision maker on whether to make some "markup snippet" an element or a helper method. Elements can also be cached, as well.

Note: beforeRender and afterRender call also be additionally invoked for each element. But you would need to manually enable those (default is false).

Test case callbacks

Please see this post for reference.

"Indirect modification" for pagination

If you still happen to use the 1.3 syntax in your 2.x apps, you might have run into something like this:

Notice (8):
Indirect modification of overloaded property PostsController::$paginate has no effect [APP/Controller/PostsController.php, line 13]

I wanted to fix this in the cake core controller, but it seems, it might be wiser to upgrade to the new PaginatorComponent syntax. If that is not possible, you can easily avoid this notice by adding this to your AppController:

/**
 * The paginate options for this controller
 *
 * @var array
 */
public $paginate = array();

This way the array is defined and accessing it the "old" way will work smoothly.

Some new PHP "flaws" I stumpled upon (and how they affect your cake app)

Yeah, there really are some ugly truths to PHP sometimes. Most can be avoided or resolved easily, though (if known!).
The following one for example has been in Cake for years (until 2.3 stable) in the FormHelper – until someone finally ran into the issue. And there will still be lots of other places where PHP itself creates a mess if not handled accordingly.

Don’t use in_array with mixed integers and strings

$result = in_array(50, array('50x'); // returns TRUE (usually unexpected)
$result = in_array('50', array('50x'); // returns FALSE (expected)
// the other way is the same (shit):
$result = in_array('50x', array(50); // returns TRUE (usually unexpected)
$result = in_array('50x', array('50'); // returns FALSE (expected)

As you can see, using the first argument without any casting can result in some unexpected results if you do not know it.
You can also use true as third param to make the comparison strict:

$result = in_array(50, array('50f5c0cf-5cd8', true); // returns FALSE (expected)

There might be some case where the above "strange" result is the expected – but in most cases probably not. It sure is one the most stupid PHP flaws I have ever seen. The string 50x is never numeric and therefore should never be casted to a numeric value here IMO.

You can also use yourself a neat little Utility method that actually works as expected here (at least the test cases pass now): Utility::inArray().

Use STRICT comparison where possible, but ALWAYS for strings

list($var1, $var2) = array("1e1", "10"); // clearly two different strings
var_dump($var1); 
var_dump($var2);
var_dump($var1 == $var2);
var_dump($var1 === $var2);
// Result:
string(3) "1e1"
string(2) "10"
bool(true) // !!!
bool(false)

Probably the most evident example is:

$x = 0;
$y = 'foo';
if ($x == $y) {
    echo 'same';
} else {
    echo 'NOT same';
}

What do you think this echos? Yeah, "same" due to the implicit int cast happening here!

Just to be clear: This is not just a bug (although I see it as one – as its the root cause of the above in_array issue!) – this is "expected behavior" in all PHP versions (including PHP5.4 and above). Unfortunately even more advanced (cake) programmers do not see the need to use strict comparison for strings. Even though, there are no downsides at all. The opposite, it is slightly faster and always more correct. I started to revise my own code regarding this just a few weeks back and also went ahead and fixed quite a few of those flaws in the core and some plugins lately.

For other types like integers or arrays it is also wise to switch to strict comparison wherever possible. It usually improves code quality in a language that is sometimes just to lose regarding type-safety. And it triggers errors/warnings earlier than it would without it if you do something wrong. So it also might cut down development time in the long run.

Some more examples:

$count = count($users); // returns int
if ($count === 1) {} // always an integer

The $count must be of the type integer (does not come from user input or database) here and therefore can be uses with strict comparison.

public function foo(array $someArray) {} // you cannot pass strings, booleans, integers, ... now

If you always expect an array you can use array typehinting. Just bear in mind it will also reduce the flexibility to pass objects witch implement ArrayAccess interface. If you only use it internally you are pretty safe to use the typehint here, though.

PhpMyAdmin

Quite a useful tool most of us use probably, locally and on the webserver.
But the default settings are usually quite annoying.
Open up /etc/phpmyadmin/config.inc.php (or similar path on other systems) and edit it accordingly:

// Maybe raise the timeout value from 5 min to at least a few hours
$cfg['LoginCookieValidity'] = 36000; // In seconds 
// The default of 30 rows per page is also a little bit low maybe
$cfg['MaxRows'] = 100;
 
4 Comments

Posted in CakePHP