March 4th, 2015Normally, when you extend a class and ovveride one method, you can change its visibility, but only to widen it. You can make a private method protected or public, and a protected method public. This is because restricting the visibility of a method would break the Liskov substitution principle as it would not allow to use the subclass in place of its superclass.
This is also true for PHP at least when you use classical inheritance, and will fail badly if you even try to override a public method with a private or protected one:
class Utility {
public function methodTwo()
{
}
}
class Driver extends Utility
{
protected function methodTwo()
{
}
}
The code above will throw a fatal error:
PHP Fatal error: Access level to Utility::methodTwo() must be public (as in class Utility) in php shell code on line 1
So, if you're trying to reduce the visibility of a method of an existing class, you're out of luck. You'll have to either override the method to make it throw a BadMethodException
, change the code of the superclass or, perhaps, just live with it.
It turns out though, that PHP supports reducing the visibility of a method defined inside a trait. So, if you know that you'll need to hide methods of a superclass, you might consider to implement such superclass as a trait:
trait Utility {
public function methodOne()
{
}
public function methodTwo()
{
}
}
class Driver {
use Utility;
protected function methodTwo()
{
}
}
And here we go:
php > $driver = new Driver;
php > $driver->methodTwo();
PHP Fatal error: Call to protected method Driver::methodTwo() from context '' in php shell code on line 1
As you see, the method has been hidden and it's unreachable from the outer context.
Nota bene
As usual with protected and private methods, you cannot use method_exists
to check if a method is callable or not, as it will return true no matter what the visibility is. You have to use is_callable
:
php > var_dump(is_callable([$driver, 'methodOne']));
bool(true)
php > var_dump(is_callable([$driver, 'methodTwo']));
bool(false)
That's it, have fun!
Tags: php
March 3rd, 2015The PSR specifications have taken the PHP community by storm, and gave different developers working on different projects a common ground to share code and improve the speed and quality of their respective works.
The PSR-1 and PSR-2 specifications, in particular, are extremely important as they suggest a common formatting standard for PHP files. While this may seem unimportant, in the days of GitHub and distributed version control, keeping a consistent coding standard can help make pull requests little, and lessens the risk of having to perform a manual merge.
Furthermore, thanks to SensioLabs, we now have a [nifty tool] (https://github.com/FriendsOfPHP/PHP-CS-Fixer) to automatically correct our code style to make it compliant with PSR-1 and PSR-2. But after using it for a while, I noticed I kept forgetting to run it before commit. The common suggestion to avoid this is to set up a precommit-hook in your working tree, but the process is a bit involved and difficult to remember.
For this reason I came up with a simple tool to automate the steps: PSR Police.
Installation
To use PSR Police you have to first install PHP-CS-Fixer system-wide.
wget http://get.sensiolabs.org/php-cs-fixer.phar -O php-cs-fixer
sudo chmod a+x php-cs-fixer
sudo mv php-cs-fixer /usr/local/bin/php-cs-fixer
Then you can install PSR Police:
phpcomposer global require tacone/psr-police
After that, make sure that ~/.composer/vendor/bin
is in your PATH.
All done!
Usage
From now on you can enforce the PSR-1/2 coding standard on any project with one command:
cd /var/www/myproject
psr-police
You just need to run the command once per project. PSR Police will kick in at every commit, and format any committed file.
A few notes:
- blade files are ignored
- only the new and modified files since the previous commit will be re-formatted
- if you clone your repository in another location, you'll need to run PSR Police again, as GIT hooks are not persisted in the remote repository
- if you have a GIT pre-commit already set-up, PSR Police won't do anything, and just spit a warning
You can remove the PSR Police hook from your repository by just running:
rm .git/hooks/
I you will find this little simple tool as useful as I do.
Tags: php, git
March 2nd, 2015This is a modified excerpt of an answer of mine on StackOverflow.
I came up with a (perhaps) better way to handle exceptions inside __toString()
:
public function __toString()
{
try {
// ... do some stuff
// and try to return a string
$string = $this->doSomeStuff();
if (!is_string($string)) {
// we must throw an exception manually here because if $value
// is not a string, PHP will trigger an error right after the
// return statement, thus escaping our try/catch.
throw new \LogicException(__CLASS__ . "__toString() must return a string");
}
return $string;
} catch (\Exception $exception) {
$previousHandler = set_exception_handler(function (){
});
restore_error_handler();
call_user_func($previousHandler, $exception);
die;
}
}
This assumes there is an exception handler defined, which is the case for most frameworks. As with the trigger_error
method, doing this will defy the purpose of try..catch, but still it is much better than dumping output with echo
. Also, many framework transform errors into exceptions, so trigger_error
won't work anyway.
As an added bonus, you'll get a full stack-trace as with normal exceptions and the normal dev-production behaviour of your framework of choice.
Works very well in Laravel, and I'm pretty sure it'll work in pretty much all the modern PHP frameworks out there.
Screenshot relevant:
note: in this example, output()
is called by a __toString()
method.
Tags: php
August 11th, 2014With Twig, the easiest way to format a result-set into a grid is through the batch filter:
{% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}
{% for row in items|batch(3) %}
<div class="row">
{% for cell in row %}
<div class="col-xs-4">{{ cell }}</div>
{% endfor %}
</div>
{% endfor %}
The example above will output a 3 column grid. Just make sure to adjust your css classes to set the cells' width accordingly.
If you want to output a table instead, you can follow this example from the official documentation:
{% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}
<table>
{% for row in items|batch(3, 'No item') %}
<tr>
{% for column in row %}
<td>{{ column }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Here's also a full example of a Bootstrap media grid, plus Laravel pagination:
{% for row in items|batch(2) %}
<div class="row">
{% for i in row %}
<div class="media col-xs-6">
{% if i.photo|trim %}
<a class="pull-left" href="/post/{{ i.id }}">
<img class="media-object" src="/uploads/{{ i.filename }}" alt="{{ i.title }}">
</a>
{% endif %}
<div class="media-body">
<a href="/post/{{ i.id }}">
<h4 class="media-heading">{{ i.title }}</h4>
</a>
<div class="date">{{ i.publication_date|date('M d, Y') }}</div>
<p>{{ Str_words(i.body|striptags, 20) }}</p>
<a href="/post/{{ i.id }}" class="read-more">Read more →</a>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
{{ items.appends(Input_except('page')).links()|raw }}
Enjoy!
Tags: twig, laravel
August 9th, 2014A common reason I hear for using Blade instead of Twig with Laravel is the need to register with Twig each PHP function you need before you can use it in your templates.
This is by design, but there's a way around, if you need it:
$twig->addFunction(new Twig_SimpleFunction('php_*', function() {
$arguments = func_get_args();
$function = array_shift($arguments);
return call_user_func_array($function, $arguments);
}
));
Now you can use whatever function you want in your templates, for example the much needed str_rot13:
rot13 with makes me {{ php_str_rot13("happy")}} !
Just in case you use Laravel and the excellent TwigBridge by Rcrowe I suggest you to register this catch all function inside start/global.php
in this way:
Event::listen('twigbridge.twig', function(Twig_Environment $twig) {
$twig->addFunction(new Twig_SimpleFunction('php_*', function() {
$arguments = func_get_args();
$function = array_shift($arguments);
return call_user_func_array($function, $arguments);
}
));
});
From now on you'll be able to use any PHP function you like by just prepending php_
to the name of the function.
Tags: twig, laravel
August 9th, 2014Here is an extremely short yet readable way to throw a "page not found" HTTP error when a record is not found in your Laravel app.
public function getPost($id)
{
$post = Article::find($id) or App::abort(404);
return View::make('frontend/post', compact('post'));
}
Laravel is special because it allows us to keep our codebase extremely concise. Since some trade-offs have been made within the framework to allow for that, we should maximize the benefit by writing in a smarter way.
Tags: laravel
August 4th, 2014Use this one liner to bulk import all the csv files inside a given directory into the mydatabase
db in Mongo.
for i in *.csv; do mongoimport -d mydatabase -c ${i%.*} --type csv --file $i --headerline ; done
The files should be plain CSV files with an header row (that is: the first line should contain the fields name). Each file will be put into a different collection named after the filename.
For example, the following CSV file addressbook.csv
:
id,name,email
1,Tommy,tommy@example.com
Will create an addressbook
collection with the following object:
{
_id : 'xxxxxxxxxxxxxxxxx',
id : 1,
name : "Tommy",
email : "tommy@example.com"
}
Enjoy!
Tags: mongodb
July 26th, 2014Here is a (long) one-liner to output a stateful select box with Laravel and the Twig Bridge Bundle:
{{ Form_select('user', {'':'Select user'} | merge(user.lists('id', 'name')),Request_get('user'), {class:'form-control'})|raw }}
In the code sample, user
is an Eloquent model, Request_get
binds automatically to the Request::get()
method of Laravel's Request facade as well as Form_select
binds to Form::select()
, the last array pushes a form-control
class to the select
tag for enhanced bootstrap styling.
Notice we are merging arrays to ensure the select box presents an empty option to beg the user to choose one:
{'':'-- Industry --'} | merge(user.lists('id', 'name'))
When the user submits the form to another page containing the same code snippet, our selectbox will appear with the right value already selected.
While this one-liner is long, somewhat redundant and a bit complicated it's good for quick copy-pastes and works with plain Laravel + Twig bridge. A Blade version would of course be very similar.
Tags: laravel