Stefano php, milano

How to hide a class method in PHP

Normally, 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

A little handy tool: PSR Police

The 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

How to handle exceptions from __toString

This 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.

__toString() exception caught by Laravel exception handler

Tags: php

How to output a grid with Twig and Boostrap

With 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 &rarr;</a>
        </div>
    </div>
    {% endfor %}
</div>
{% endfor %}

{{ items.appends(Input_except('page')).links()|raw }}

Enjoy!

Tags: twig, laravel

How to use any PHP function in Twig

A 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

How to throw a 404 error if a record is not found.

Here 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

Import Many Csv Files In Mongo

Use 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

Stateful select box in one line with Laravel and Twig

Here 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