Category: PHP


Debugging a Twig View

Just a quick note here to save future hunting.

Ever seen this crap error message?

An exception has been thrown during the rendering of a templat

It totally sucks, right? And then you try debugging with XDebug, only to find you can’t most of the time, since Twig uses eval().

Anyway, to save you looking, if you set a breakpoint where that message is thrown you can get to see your own Exception:

vendor/twig/twig/lib/Twig/Template.php

Look at line 401 (this may have changed in later versions):

} catch (Exception $e) {
    throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getTemplateName(), $e);
}

You can now see your Exception!

In PHPStorm, you can do a regex find and replace. To upgrade a crappy old site using <?, just use the following regex:

#<\?(?!=|php|xml)#

https://regex101.com/r/yD6dK5/5

DOMDocuments are cool, and a really nice way of dealing with HTML in an OO fashion. However, sometimes, we have an HTML string element which we need to add to our Document. Here’s how you do it:

    function createNodesFromHTML(DOMDocument $doc,$str) 
    {
        $nodes = array();
        $d = new DOMDocument();
        $d->loadHTML("<html>{$str}</html>");
        $child = $d->documentElement->firstChild->firstChild;
        while($child) {
            $nodes[] = $doc->importNode($child,true);
            $child = $child->nextSibling;
        }
        return $nodes;
    }


        $dom = new DOMDocument();
        $icon = '<i class="fa fa-remove"></i>'; // This is our string

        $button = $dom->createElement('a');
        $button->setAttribute('href', '/whatever/delete/12345');
        $button->setAttribute('class', 'btn btn-sm btn-danger');
        
        // This is us turning the string(s) into nodes we can add
        $nodes = createNodesFromHTML($dom, $icon);
        $button->appendChild($nodes[0]);
        
        $dom->appendChild($button);
        echo $dom->saveHTML();

As any decent developer knows, register_globals was a terrible idea, a security risk, and turned ON by default in old versions of PHP!

Thankfully it was removed in PHP 5.4. However, if you are stuck developing on a site that used register_globals, you may find yourself in a situation where seemingly you can’t upgrade beyond PHP 5.3.

However, it’s not all bad news, we can put a piece of code in place which emulates register_globals. This will let us turn it off. It still means your code is less than secure, but of course that’ll be fixed in time as you upgrade and refactor the site, right?

To emulate register_globals, just add the following code to one of your initialisation/bootstrap scripts:

// Emulate register_globals on
if (!ini_get('register_globals')) {
    $superglobals = array($_SERVER, $_ENV,
        $_FILES, $_COOKIE, $_POST, $_GET);
    if (isset($_SESSION)) {
        array_unshift($superglobals, $_SESSION);
    }
    foreach ($superglobals as $superglobal) {
        extract($superglobal, EXTR_SKIP);
    }
}

Now you can turn it off in php.ini. Why is it so bad though? Well, have a look at this:

code

Looks like nothing should happen on that page, right? nothing has been defined.

WRONG! try adding ?loggedIn=anything to the end of the URL:

loggedin

As you readers probably know, I can’t stand XAMPP and MAMP, being two steaming piles of crap, and have long advocated that you set up VirtualBox & Vagrant, then head over to http://www.puphpet.com, fill in the forms to configure your VM,  generate the config.yaml, and then unzip it and run ‘vagrant up’ to install it. Brilliant so far.

Yesterday I had a total downer of a day, trying to run an old legacy PHP 5.3 app. PuPHPet doesn’t have the EOL PHP 5.3, so at first I settled as a one off for MAMP, but it was slow and horrible.

Then I thought, wait! If I don’t configure Apache or PHP in puphpet, I could get a box up and install 5.3 myself. That’s when I discovered the awesomeness of the puphpet/files folder.

The only thing I used in there was the ssh keys. But there are empty folders waiting for .sh files (shell scripts) to be dropped in.

So for this box, I created exec-once/install-stuff.sh which contained the following:

#!/bin/bash
yum -y install httpd php
yum -y install php-mysql php-devel php-gd php-pecl-memcache php-pspell php-snmp php-xmlrpc php-xml

Then upon running vagrant provision, it not only looked for changes in config.yaml, but it checks for changes in these files too!

I then made set-vhosts.sh, and import-database.sh, which look like these:

#!/bin/bash
echo "
===========================================
Adding vhosts to /etc/httpd/conf/httpd.conf
===========================================
"
echo "
<VirtualHost *:80>

   DocumentRoot /var/www/fife/web
   ServerName fife
   ErrorLog /var/www/fife/log/error.log

   <Directory "/var/www/fife">
      Options -Indexes +FollowSymLinks
      Order allow,deny
      Allow from all
      AllowOverride All
  </Directory>

</VirtualHost>
" >> /etc/httpd/conf/httpd.conf

And …

#!/bin/bash
mysql -u root --password=123 --database=fortdev < /var/www/fife/data/sql_scripts/symf_fortdev.sql

I take it by now you get the idea! So now you can totally destroy your VM, and put any customisations in these shell scripts, so your full setup can be back up in 5 minutes flat with a vagrant up and vagrant provision!!!

You can then also start thinking about using puPHPet for deploying your setup to your production server 🙂 There’s a vagrant plugin called Vagrant Managed Servers, which will take care of that for you. https://github.com/tknerr/vagrant-managed-servers . I haven’t looked at it yet, but of course you can expect a blog post on it here when I figure it all out!!

This is real easy, but i keep forgetting which option to use!

If you have separated some of your code into a composer vendor package, and are currently using it in a project, it can be annoying if you need to update it. First you need to open that project up, make your changes, commit, push, wait for tests to pass on travis etc, tag a new version (depending), update packagist if it hasn’t automatically already, and then you can go back into composer and update.

So to save that hassle, composer has the –prefer-source option (–prefer-dist is the one that confused me). This puts the .git folder in your vendor package folder, allowing you to edit, commit, and push from there. Much better.

If you already have the package installed, just delete it. If you haven’t installed it yet, just require it. Both with the –prefer-source option.

composer require delboy1978uk/user --prefer-source
// or
composer update delboy1978uk/user --prefer-source

Replacing my own package above with the one you need, of course. Have fun!Screen Shot 2016-01-14 at 20.26.55

After composer requiring doctrine/migrations, you need to edit your cli-config.php. Here’s mine, with the new stuff added in bold.

<?php
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use XYZ\XYZService;
use XYZ\Test\XYZTesting;

// This is just a dependency injection container
$container = XYZTesting::getContainer();

/* Edit these details to suit

$container['db.credentials'] = array(
    'driver' => 'pdo_mysql',
    'dbname' => 'twg',
    'user' => 'dbuser',
    'password' => '123',
];
 */

// this is just my service which returns the entitymanager (requires the DIC above)
$svc = new XYZService($container);
$em = $svc->getEntityManager();
$helperSet = ConsoleRunner::createHelperSet($em);

// Add Doctrine Migration commands
$cli = ConsoleRunner::createApplication($helperSet,[
    new \Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand(),
    new \Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand(),
    new \Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand(),
    new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand(),
    new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand(),
    new \Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand(),
]);

return $cli->run();

Note that I have removed the last line, return ConsoleRunner::createHelperSet($entityManager); and replaced it with return $cli->run(); Now if you type doctrine:

$ doctrine
Doctrine Command Line Interface version 2.5.1

Usage:
command [options] [arguments]

Options:
-h, --help            Display this help message
-q, --quiet           Do not output any message
-V, --version         Display this application version
--ansi            Force ANSI output
--no-ansi         Disable ANSI output
-n, --no-interaction  Do not ask any interactive question
-v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
help                            Displays help for a command
list                            Lists commands
dbal
dbal:import                     Import SQL file(s) directly to Database.
dbal:run-sql                    Executes arbitrary SQL directly from the command line.
migrations
migrations:diff                 Generate a migration by comparing your current database to your mapping information.
migrations:execute              Execute a single migration version up or down manually.
migrations:generate             Generate a blank migration class.
migrations:migrate              Execute a migration to a specified version or the latest available version.
migrations:status               View the status of a set of migrations.
migrations:version              Manually add and delete migration versions from the version table.
orm
orm:clear-cache:metadata        Clear all metadata cache of the various cache drivers.
orm:clear-cache:query           Clear all query cache of the various cache drivers.
orm:clear-cache:result          Clear all result cache of the various cache drivers.
orm:convert-d1-schema           Converts Doctrine 1.X schema into a Doctrine 2.X schema.
orm:convert-mapping             Convert mapping information between supported formats.
orm:convert:d1-schema           Converts Doctrine 1.X schema into a Doctrine 2.X schema.
orm:convert:mapping             Convert mapping information between supported formats.
orm:ensure-production-settings  Verify that Doctrine is properly configured for a production environment.
orm:generate-entities           Generate entity classes and method stubs from your mapping information.
orm:generate-proxies            Generates proxy classes for entity classes.
orm:generate-repositories       Generate repository classes from your mapping information.
orm:generate:entities           Generate entity classes and method stubs from your mapping information.
orm:generate:proxies            Generates proxy classes for entity classes.
orm:generate:repositories       Generate repository classes from your mapping information.
orm:info                        Show basic information about all mapped entities
orm:mapping:describe            Display information about mapped objects
orm:run-dql                     Executes arbitrary DQL directly from the command line.
orm:schema-tool:create          Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.
orm:schema-tool:drop            Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.
orm:schema-tool:update          Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata.
orm:validate-schema             Validate the mapping files.

Loads more commands! Now you can start migrating your database properly and safely 🙂 Have fun!

Update
After having run my migrations, it turns out it dumps the migration files in your doc root! After a bit of faffing about, I got the config working, so here is the full cli-config.php:

<?php

use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand;
use Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use XYZ\XYZService;
use XYZ\Test\XYZTesting;

$container = XYZTesting::getContainer();

/* Edit these details to suit

$container['db.credentials'] = array(
    'driver' => 'pdo_mysql',
    'dbname' => 'XYZ',
    'user' => 'dbuser',
    'password' => '123',
];
 */

// Fetch the entity Manager
$svc = new XYZService($container);
$em = $svc->getEntityManager();

// Create the helperset
$helperSet = ConsoleRunner::createHelperSet($em);

/** Migrations setup */

$configuration = new Configuration($em->getConnection());
$configuration->setMigrationsDirectory('migrations');

$diff = new DiffCommand();
$exec = new ExecuteCommand();
$gen = new GenerateCommand();
$migrate = new MigrateCommand();
$status = new StatusCommand();
$ver = new VersionCommand();

$diff->setMigrationConfiguration($configuration);


$cli = ConsoleRunner::createApplication($helperSet,[
    $diff, $exec, $gen, $migrate, $status, $ver
]);

return $cli->run();

Because this is the way you ensure your code is awesome, and provably works!

If you you have some code you think could be reused in lots of projects, create a repository in Github and share it with the world!

I set up a project on github called delboy1978uk/blank. It’s literally a blank PHP project waiting to be written.

However it already has configuration for Travis for automated testing, and it also hooks up with Scrutinizer, which will analyse the code coverage report Travis sends it,  giving you reports on how many of your class methods have been tested, and the quality of your code (less complex methods = good!).

You can clone the repository, rename a folder and a couple of files, and search and replace my blank class name refs, then you are good to go! Just composer install and you can run tests by running:

vendor/bin/codecept run unit --coverage-html

The coverage option generates an html report you can browse in the tests/output folder.

If you try it straight away you’ll see there is one test in tests/unit/del/BlankTest.php. Tests usually follow this pattern. If it says you pass a string as an argument, and return some object, then test it! The simplest example is a getter/setter. If you run setsomething(‘hello’); then getSomething() should be ‘hello’. Most tests say something like $this->assertTrue() or $this->assertInstanceOf() or $this->assertEquals(). You get the idea. Check the code coverage reports to ensure the tests cover all eventualities. But it’s great seeing your code working, with tested PROOF that it works.

So to summarise:

  • Create your new repository on Github, activate your new repository in Travis CI , and activate your new  repository in Scrutinizer
  • Clone https://github.com/delboy1978uk/blank somewhere and delete the .git from it
  • Clone https://github.com/you/your-project somewhere-else
  • Copy everything from somewhere to somewhere-else (including hidden files)
  • Tweak the composrr.json, rename the class file and test file, search and replace refs
  • Commit & Push 😀
  • Register your project on Packagist!

All that should be doable in 5 minutes, so you can instantly get set up ready to code the real stuff! Try writing your assumptions first in the test file!

 

puPHPet Box MySQL backup

As you all know by now, I love puPHPet. And I like tinkering with things. Which of course leads to the occasional breaking of things. And then I realised, okay, your sites files are being mounted into this virtual machine, what about the databases though?

The great thing about puPHPet and Vagrant is you can build a full server up from scratch in minutes. The one downside is that it cant be smart enough to uninstall things. If you add lines to your config.yaml it will install something, but removing those lines means puPHPet won’t even know it was there, so how would it know to uninstall it? The exceptions of course being the ones which have their own config.yaml entry, with an install: ‘1’ or install: ‘0’

Anyway, I generated me a new config and was about to blitz my old VM when the usual gut instinct checks kicked in, and the DB sprung to mind immediately. So I made a quick backupdbs.sh shell script (courtesy of a StackOverflow post) which makes individual sql files for each of your DB’s. I decided I would keep the script in my mounted sites folder in a bin directory. So hence it lives in /var/www/bin/backupdbs.sh

#!/bin/bash

USER="root"
PASSWORD="YOURPASSWORDHERE"

databases=`mysql -u $USER -p$PASSWORD -e "SHOW DATABASES;" | tr -d "| " | grep -v Database`

for db in $databases; do
    if [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]] ; then
        echo "Dumping database: $db"
        mysqldump -u $USER -p$PASSWORD --databases $db > `date +%Y%m%d`.$db.sql
    fi
done

To back everything up, vagrant ssh into the machine, then (you’ll probably need to switch user to www-data) do the following:

sudo su www-data
cd /var/www/bin
./backupdbs.sh

Now you’ll find all your DB’s SQL files date stamped in the folder! You can continue to break, tweak, fix, and run your VM to your hearts content 😀

Custom Magento EAV Attributes

Magento has an Entity Attribute Value system in the db, neaning you can easily add custom stuff by adding rows and not columns! But that’s all done for us, so here’s how we set it up. In this example I’m wanting a home page image radio button.

In the config.xml you’ll see something like

<version>0.2.0</version>

So in this example we would bump that up to 0.3.0. Also, check you have a setup key in the xml:

<resources>
    <madskull_homepage_setup>
        <setup>
            <module>Madskull_HomePage</module>
            <class>Mage_Catalog_Model_Resource_Setup</class>
        </setup>
    </madskull_homepage_setup>
</resources>

Next, in the sql folder of your module, create a file called madskull_homepage_setup/upgrade-0.2.0-0.3.0.php (thats upgrading from v0.2.0 to v0.3.0) and I put the following:

<?php

/* @var $installer Mage_Catalog_Model_Resource_Setup */
$installer = $this;

$installer->startSetup();

$installer->addAttribute(
    Mage_Catalog_Model_Product::ENTITY,
    'homepage_image',
    array(
        'frontend'                      => 'catalog/product_attribute_frontend_image',
        'global'                        => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL,
        'visible'                       => true,
        'used_in_product_listing'       => true,
        'filterable'                    => false,
        'filterable_in_search'          => false,
        'used_for_price_rules'          => false,
        'searchable'                    => false,
        'comparable'                    => false,
        'visible_on_front'              => true,
        'used_for_sort_by'              => false,
        'is_visible_in_advanced_search' => false,
        'type'                          => 'varchar',
        'input'                         => 'media_image',
        'group'                         => 'Images',
        'label'                         => 'Homepage Image',
        'user_defined'                  => true,
        'is_configurable'               => false,
        'required'                      => false
    )
);

$installer->endSetup();

When you next visit any page, Magento will see the version has changed, and attempt to upgrade it. If successful, it will appear in the eav_attribute table. Clear your caches,  and reindex. Now if you go into your products in the admin panel, and select any product to edit, click on the Images option, and you should see our new attribute in there as a radio button! 🙂

Now you can use your variable by doing things like $collection->addAttributeToSelect([‘name’, ‘price’, ‘special_price’, ‘homepage_image’]);  and so on! 😀