<![CDATA[Martin Hujer blog]]> 2018-08-26T13:57:50+00:00 https://blog.martinhujer.cz/ Sculpin <![CDATA[Use different Google Analytics ID by domain in Google Tag Manager]]> 2018-08-26T00:00:00+00:00 https://blog.martinhujer.cz/use-different-google-analytics-id-by-domain-in-tag-manager/ When setting up Google Analytics (or Facebook Pixel) in the Google Tag Manager the typical approach is to put the UA-code or the Pixel ID into the Constant Variable. And that ID is then used in the GA/Facebook Tag.

But soon after launch the people from Marketing will start complaining that some strange orders and conversions are appearing in the Google Analytics.

What happened? Apart from real orders, also the orders made in testing and dev environments are sent there.

There is a better way. You can easily use different UA codes in production and in testing environments. In the rest of the article I will guide you through setting it up.

Analysis

It is clear that we want to use testing UA code in dev/test environments, but apart from that we also want to use it when testing the Google Tag Manager configuration itself with Preview feature (even on production website).

The decision process for the UA code goes like this:

  • Are we running in Debug mode?
    • YES: use test ID
    • NO:
    • Are we on production domain?
      • YES: use real ID
      • NO: use test ID

Preparation

  1. Go to Google Tag Manager and create two Constant Variables:

    • GA_ID_DEV with the testing ID
    • GA_ID_PROD with the production ID
  2. In the Variables section in Built-In Variables enable Debug Mode and Page Hostname variables.

Setting up the hostname detection

  1. Create a Variable of type Lookup Table and name it GA_ID_BY_HOSTNAME
  2. Input Variable is {{Page Hostname}}
  3. Click Add row
  4. Put your production domain into the Input field (just the domain without https:// and the trailing /)
  5. Put {{GA_ID_PROD}} into the Output field
  6. Check Set default value checkbox
  7. Put {{GA_ID_DEV}} to the Default value field

It should look like this: GA_ID_BY_HOSTNAME variable configuration

By defining only the production domain and using default value for anything else, we handle any testing or dev environment.

Setting up the Debug environment detection

If you look back into the Analysis chapter, we have covered the Are we on production domain? part. Now we will cover the Are we running in Debug mode?.

  1. Create a Variable of type Lookup Table and name it GA_ID
  2. Input Variable is {{Debug Mode}}
  3. Click Add row
  4. Put true into the Input field and {{GA_ID_DEV}} into the Output field
  5. Click Add row again
  6. Put false into the Input field and {{GA_ID_BY_HOSTNAME}} into the Output field

It should look like this: GA_ID variable configuration

You might have noticed that we have used the variable GA_ID_BY_HOSTNAME which we prepared in previous step.

Setting up the Google Analytics Tag

With all those variables prepared, you can set up the Google Analytics tag as usual. But when creating the Google Analytics Settings, put {{GA_ID}} into the Tracking ID field as in the following picture. It will be resolved to correct UA code depending on the environment.

Google Analytics Settings variable

That's it!

Conclusion

Apart from Google Analytics ID, you can use this guide to set up Facebook Pixel ID or any other system where you can easily create different IDs for different environments.

Are you using similar approach? Or did you find a better way? Let me know in the comments!

]]>
<![CDATA[Consistence brings consistency to the PHP]]> 2018-02-06T00:00:00+00:00 https://blog.martinhujer.cz/consistence-brings-consistency-to-the-php/ In the article I describe the Consistence library that aims to bring consistency to PHP applications.

There is no argument, that PHP can sometimes be a bit inconsistent about naming stuff and maintaining order of parameters for related functions. Also, in some cases it is not strict and allows you to use the language and the functions in a wrong way. Sometimes you get false as a return value where an exception would be appropriate.

I really like the idea of code being strict because it prevents errors in the application. E.g. strpos usually returns int, but it returns false when the $needle was not found. It would be much better to throw SubstringNotFoundException in that case.

Consistence provides opinionated strict wrappers with better error handling and consistent naming and consistent parameters order.

Disclaimer: I haven't created the library, but I find it useful and I hope you will as well. It is one of the packages I use on every project.

Let's dive into it.

Use Enums for better type safety

It's a good practice to use constants instead of passing magic numbers (or magic strings) around. But you still pass them as a string, so there is nothing that prevents you from passing any other arbitrary string:

<?php
class BodyType
{
    const SUV = 'suv';
    const COMBI = 'combi';
    const HATCHBACK = 'hatchback';
    const CABRIO = 'cabrio';
    // ... 
}

function createCar(string $bodyType) {
    // do something with $bodyType
}

$car = createCar(BodyType::CABRIO);
$car = createCar('doubledecker'); // unexpected things can happen

Consistence provides an enum implementation. When you extend the class from Enum, all constants are automatically treated as possible enum values:

<?php
class BodyType extends \Consistence\Enum\Enum
{
    const SUV = 'suv';
    const COMBI = 'combi';
    const HATCHBACK = 'hatchback';
    const CABRIO = 'cabrio';
    // ...
}

function createCar(BodyType $bodyType) {
    echo $bodyType->getValue(); // you can use `getValue()` method to access the internal enum value
}

// use get() to get an instance of enum
$cabrioBodyType = BodyType::get(BodyType::CABRIO);

// It is a regular class instance
var_dump($cabrioBodyType instanceof BodyType); // bool(true)

// You always get the same instance, so you can compare them
var_dump(BodyType::get(BodyType::CABRIO) === BodyType::get(BodyType::CABRIO)); // bool(true)

// Type-hint checks work as expected
$car = createCar($cabrioBodyType); 

// Argument 1 passed to createCar() must be an instance of BodyType, string given
$car = createCar('doubledecker'); 

Enums also make it easier to write an actual code. Compare the following examples. When you want to instantiate the class from the first one, you have to dig into the documentation to check what are the allowed values:

<?php
class Car
{
    public function __construct(
        string $bodyType,
        string $transmissionType,
        string $engineType,
        string $fuelType
    )
    {}
}

In the second one, you just start typing BodyType::get(BodyType:: and then choose from the suggested values:

<?php
class Car
{
    public function __construct(
        BodyType $bodyType,
        TransmissionType $transmissionType,
        EngineType $engineType,
        FuelType $fuelType
    )
    {}
}

There are Doctrine and Doctrine + Symfony integrations which allow you to use Enums in Doctrine entities:

<?php
use Consistence\Doctrine\Enum\EnumAnnotation as Enum;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 */
class Car
{
    /**
     * @Enum(class=BodyType::class)
     * @ORM\Column(type="string_enum")
     * @var BodyType
     */
    private $bodyType;

    public function __construct(BodyType $bodyType)
    {
        $this->bodyType = $bodyType;
    }

    public function getBodyType(): BodyType
    {
        return $this->bodyType;
    }
}

If you need to represent multiple values, have a look at the MultiEnums.

If the Consistence only provided the Enums, it would be enough reason for me to use it.

Use ObjectPrototype to disable magic methods

In PHP you can write to undefined object properties. If the property is not defined and you assign a value to it, it is created in runtime.

I think it is a bad idea because it can hide subtle bugs and typos. Consider the following example, where I made a several mistakes in the code, but PHP almost did not complain.

<?php
class MyClass
{
    public $foo;
}

$a = new MyClass();
$a->boo = 'bb'; // I made a typo, but PHP is fine with that

var_dump($a->foo);  // NULL
var_dump($a->boo);  // string(2) "bb"
var_dump($a->otherProperty);  // Notice:  Undefined property: MyClass::$otherProperty

If you want a more real-world example, consider this:

<?php
$finalPrice = $product->price * $customer->distountRate;

There is a typo in a property name, so the $finalPrice would be 0, because undefined property has null value which is converted to 0 and used in multiplication.

Consistence provides a straightforward way to mitigate this type of bugs. There is an ObjectPrototype class which you can extend your classes from. If you try to assign or read something from an undefined property, an exception is thrown. Calling undefined methods throws an exception too. Those exceptions are not meant to be caught, but they should be fixed directly in your code.

<?php
class MyClass extends \Consistence\ObjectPrototype
{
    public $foo;
}

$a = new MyClass();
$a->boo = 'bb'; // throws Consistence\UndefinedPropertyException

var_dump($a->foo); // NULL, but that does not matter, an exception would be thrown before
var_dump($a->boo); // throws Consistence\UndefinedPropertyException
var_dump($a->otherProperty); // throws Consistence\UndefinedPropertyException

$a->foo(); // throws Consistence\UndefinedMethodException

You're probably thinking, that using a base class for all classes is an awful code-smell!

I agree in general, but this case is different. If you consider the Consistence library to be an extension or augmentation of the standard PHP library, ObjectPrototype class is more like Object base class in Java. And nobody complains about it there.

The unseen advantage of this is that when you extend ObjectPrototype, you can't extend anything else, which is a good thing. You should be using composition as a code reuse mechanism anyway. In cases when the inheritance makes sense, you can use ObjectMixinTrait which enables the strict behaviour. Good example are the exceptions (they are objects too!):

<?php
class MyCustomException extends \Exception
{
    use \Consistence\Type\ObjectMixinTrait;
}

$exception = new MyCustomException();
$exception->foo = 'a'; // throws Consistence\UndefinedPropertyException

Speaking of exceptions, there is a PhpException which can be used as their base class. It has a shorter constructor without the mostly useless $code argument.

Strict type checking

PHP type-hints are powerful, but there are still a lot of things they can't check. You can put the detailed type info into the PHPDoc, but it is ignored at runtime. It only helps other developers and the IDE to better understand the code.

In the following example, IDE understands that the array contains int values, but PHP only checks that it is an array:

<?php
/**
 * @param int[] $data
 */
function foo(array $data)
{
    var_dump($data);
}

foo([0, 1, 2]); // OK,
foo(['0', '1', '2']); // OK
foo(1); // not OK: Argument 1 passed to foo() must be of the type array, integer given

You can use Type class to manually check that the variable contains expected type:

<?php
use Consistence\Type\Type;

/**
 * @param int[] $data
 */
function foo(array $data)
{
    Type::checkType($data, 'int[]');

    var_dump($data);
}

foo([0, 1, 2]); // OK 
foo(['0', '1', '2']); // throws Consistence\InvalidArgumentTypeException: int[] expected,  [array] given

Type::checkType() throws an exception when the type-check fails. If you want to throw the exception yourself, you can use Type::hasType($data, 'int[]') which returns boolean value.

Here are some examples what else you can use as an expected type:

  • object: useful in < PHP 7.2
  • Union types: int|string or int[]|string[]
  • Collection of objects: Product::class . '[]'
  • Array keys type: int:string[] (array of string values indexed by int keys)

Consistent array manipulation functions

Standard PHP functions for array manipulation aren't very consistent or strict:

  • most of them are not using strict comparison - they do automatic type conversion
  • most of them do not accept callbacks to create more complicated logic
  • some accept the source array as first parameter, some as a second parameter

Consistence provides several array manipulation functions in the ArrayType namespace. Let's have a look at the examples.

In the first example you can see how the implicit type conversion in in_array function can lead to unexpected results:

<?php
use Consistence\Type\ArrayType\ArrayType;

$data = ['1', '2'];

// automatic type conversion
in_array('1', $data); // bool(true)
in_array(1, $data); // bool(true) <- unexpected result!
in_array(true, $data); // bool(true) <- unexpected result!

// strict flag set to true
in_array('1', $data, true); // bool(true)
in_array(1, $data, true); // bool(false) <- works as expected
in_array(true, $data, true); // bool(false) <- works as expected

// ArrayType::containsValue() works as expected by default
ArrayType::containsValue($data, '1'); // bool(true)
ArrayType::containsValue($data, 1);  // bool(false)
ArrayType::containsValue($data, true);  // bool(false)

In this case you can get away with just making sure to always add true as a third parameter.

The code gets more complicated when you need more complex logic, e.g. you want to check if the array contains a value larger than 3. You can iterate through the array and determine if some value matches the condition:

<?php
$data = [1, 2, 3, 4];

$result = false;
foreach ($data as $value) {
    if ($value > 3) {
        $result = true;
        break;
    }
}
var_dump($result); // bool(true)

Consistence provides more succinct way of doing it with containsValueByValueCallback(), where you only need to write the actual business logic and not the repetitive boilerplate code:

<?php
use Consistence\Type\ArrayType\ArrayType;

$data = [1, 2, 3, 4];

$result = ArrayType::containsValueByValueCallback($data, function (int $value) {
    return $value > 3;
});

var_dump($result); // bool(true);

I often use array filtering by callback:

<?php
use Consistence\Type\ArrayType\ArrayType;

$data = [1, 2, 3, 4];

$result = ArrayType::filterValuesByCallback($data, function (int $value): bool {
    return $value > 2;
});
var_dump($result); // [3, 4]

And mapping values by callback:

<?php
use Consistence\Type\ArrayType\ArrayType;

$data = [1, 2, 3, 4];

// map values using callback
$result = ArrayType::mapValuesByCallback($data, function (int $value) {
    return $value * 2;
});
var_dump($result); // [2, 4, 6, 8]

Sometimes it may be convenient to use filterByCallback() and mapByCallback() that pass both key and value to the callback function (they use the KeyValuePair value object internally).

Did you know, that array_unique() is always comparing loosely? Therefore, I prefer to use strict ArrayType::uniqueValues():

<?php
use Consistence\Type\ArrayType\ArrayType;

$data = [1, '1', true];

var_dump(array_unique($data)); // ['1']
var_dump(ArrayType::uniqueValues($data)); // [1, '1', true]

Last thing I want to show you regarding the arrays is the find/get convention. If the method name starts with find (e.g. findValue()), it can either return the value or null. But if the method name starts with get (e.g. getValue()), it either returns the value or throws an exception.

<?php
use Consistence\Type\ArrayType\ArrayType;

$data = [1, 2, 3, 4];

// we want to get a value at key

ArrayType::findValue($data, 3); // int(4)
ArrayType::findValue($data, 5); // null

ArrayType::getValue($data, 3); // int(4)
ArrayType::getValue($data, 5); // throws ElementDoesNotExistException

And there is more, have a look at the available methods for yourself.

Regex

This chapter is going to be a short one. Consistence provides a preg_match wrapper with more sensible API and exceptions error handling:

<?php
use Consistence\RegExp\RegExp;

// you can either check if the string matches the pattern
RegExp::matches('abc', '/bc+/'); // bool(true)

// or get the matches back
$matches = RegExp::match('abcde', '/[b-d]+/');
var_dump($matches); // ['bcd']

PHPStan integration

There is a static analysis tool called PHPStan (you can read more about it in Ondřej's blogpost.

I have created PHPStan rules for the Consistence library. There is one which checks that the class extends ObjectPrototype or uses ObjectMixinTrait. And second which checks that you are using safer Consistence array manipulation functions instead of the plain native PHP ones.

Conclusion

In the article I've described the most interesting parts of the Consistence library. My favourite are enums and strict types everywhere.

If you are not using the Consistence library yet, give it a try!

]]>
<![CDATA[Málaga - co kdy, kde a jak? Podruhé.]]> 2018-01-27T00:00:00+00:00 https://blog.martinhujer.cz/malaga-podruhe/ Letos v lednu (12. - 26.1.) jsem znovu vyrazil do Málagy za teplem. Našel jsem spoustu dalších zajímavých věcí, které stojí za to navštívit. Navazuji na předchozí článek o Málaze, takže si určitě pročtěte i ten.

Noční Málaga

Doprava

  • Uber v Málaze nefunguje, ale můžete místo něj použít Cabify.

  • Ve městě jezdí spoustu autobusů, spoje jdou vyhledávat v Google Maps. Nejpohodlnější je koupit si kartu na autobus (€1.7) a dobít si 10 jízdami za €8.30. Kartu jsem kupoval v prodejně v centru.

  • Teoreticky by na tu kartu na autobus mělo jít za €5 aktivovat možnost půjčování kol Malagabici. Nedá se to vyřešit v té prodejně v centru (kde jsem kupoval kartu), ale je potřeba dojít do kanceláře na Estación de María Zambrano. Jo a prý tam jsou asi jen do 15 h (nemám vyzkoušeno).

  • Z letiště i zpátky na letiště jsem jel vlakem, lístek stojí 1.8€. Zpátky jsem letěl v 7:00 ráno s Ryanairem a v pohodě stačilo jet vlakem v 5:20. Pokud byste chtěli větší rezervu, tak bych doporučoval to Cabify.

Co navštívit

Enflish Cemetery

  • Museo de Málaga - parádní muzeum. V prvním patře umění (vynechal jsem), v druhém patře archeologie (super). Vstup zdarma, zavřeno v pondělí.

  • Parque de Málaga - park v centru, víceméně je to botanická zahrada s fontánkami.

  • Parque del Oeste - obrovský park v Huelinu, je v něm obrovské jezírko s fontánou a želvami, palmy, papoušci, pštrosi a tak.

Parque del Oeste

  • Plaza de toros de La Malagueta - býčí aréna, myslím že se tam něco děje jen v srpnu. Zvenku to moc zábavné není. Ale je na ní super výhled shora. Viz další tip.

Malagueta

  • Mirador de Gibralfaro - na tuhle vyhlídku narazíte cestou na Castillo de Gibralfaro. Je z ní super výhled na město, moře i arénu. Zajímavost je, že většina lidí, co tam dorazí, se nejdříve otočí zády a fotí selfie místo toho, aby se koukali. Z vyhlídky kromě přímé cesty na hrad vede i trošku okružní turistická, odkud je ještě lepší výhled na Malaguetu než z té vyhlídky. A cestu na hrad to prodlouží jen o pár set metrů. Tady místo Google Maps doporučuji offline Mapy.cz, kde je zanesená i tahle turistická trasa.

  • Mirador de Alcazaba - vyhlídka před Alcazabou. Jde se tam po schodech za vlevo za vstupem do Teatro Romano. Zadarmo.

  • Teatro Romano - římský amfiteátr. Má docela zajímavou historii, objevili ho až někdy v půlce 20. století, když dělali terénní práce u dostavěného Casa de Cultura. Ten nakonec zbourali (v roce 1995), protože zjistili, že kus toho amfiteátru je pod ním. A Teatro Romano otevřeli k prohlídkám až v roce 2011.

  • Alcazaba - arabská pevnost, hezky zachovalá. Vstupné myslím 2.5€.

  • Mercado de Huelin - trh s ovocem, zeleninou, masem, rybami a mořskými plody. Typicky tam chodí jen místní.

  • La Taberna del Pintxo - tapas bar, kde hlavní roli hrají "Pintxos" (čti pinčos). Jsou to vlastně obložené chlebíčky, ale hodně vymazlené. A mají "párátko". Funguje to tak, že si sami chodíte na bar a vybíráte si z nabídky chlazených. A zároveň nosí z kuchyně tácy s teplými pintxos, prochází mezi stoly a pokud chcete, tak si nějaké vezmete. A při placení vám spočítají, kolik máte "párátek". Jo a mají tam na výběr i minidortíky.

  • Santa Canela Café-Crepería - výborná hipster kavárna ve centru. Kromě standardních espressových káv umí i různé filtrované (Chemex, V60, aeropress). A mají v nabídce i různé druhy kávy (zrnek).

Kavárna Santa Canela

  • El Último Mono Juice & Coffee - dobrá káva v centru. Nabízejí i smoothie apod.

  • La Esperanza de los Ascurra - jedna z mnoha restaurací. Tuhle doporučila Airbnb hostitelka z bytu, kde bydlela ségra s partou, když přijeli na víkend do Málagy. Mají fajn nabídku mořských plodů (třeba chobotnici - pulpo). Takže pokud stejně jako já máte problém si vybrat kam jít, tak můžete třeba sem.

  • Café Club Carambuco - fajn kavárna v Huelinu. Mají dobrou kávu, zapečené bagety a taky různé dortíky, byl jsem tam několikrát.

Café Club Carumbuco

  • Desembocadura del Guadalhorce - epická přírodní rezervace s mokřady. Mají tam dokonce pozorovatelny na sledování ptáků. Za mě jedna z top věcí v Málaze. Akorát to je trochu daleko a cestou si trochu zajdete, protože tam kde by se to hodilo, není most. Opět doporučuju Mapy.cz, kde jsou vyznačené i jednotlivé pozorovatelny. Nejlepší byla Observatorio Laguna Grande.

Výhled na lagunu v Desembocadura del Guadalhorce

  • Monte San Antón - baví mě najít nějaký kopec a vylézt na něj. Tady jsem našel jeden, na který se dá docela hezky dojít, je to jen 500 výškových metrů. Dojel jsem autobusem dole pod kopec a vyšlápnul si to nahoru. Část cesty je skrz hodně pěknou vilovou čtvrť. Nahoru a dolů mi to zabralo 3 h včetně půlhodinové svačiny a rozhlížení se nahoře. Cestou se jde skrz hezkou vilovou čtvrť. Prý je to top místo, kde se dá v Málaze bydlet, domy stojí od milionu eur výš. Záznam trasy na Wikiloc. (na fotce je výhled na Málagu)

Výhled na Málagu ze San Antón

  • Restaurante Hermanos Muñoz - když jsem sešel dolů z kopce, tak jsem dostal chuť dát si něco dobrého. Vybral jsem tuhle restauraci a bylo to super. Měl jsem rosadu, což je nějaká ryba. A hlavně jsem zkusil espeto de sardinas, což jsou sardinky pečené na ohni napíchnuté na tyčce. Jí se tak, že oloupete maso z obou stran páteře (jí se i ta opečená kůže) a necháte hlavu, ocas a páteř.

Sardinky - before

Sardinky - after

  • Chiringuito je obecný název pro restauraci/stánek s jídly z ryb a mořských plodů u pláže. Ty sardinky typicky mají ve všech.

  • Merendero Casa Jose - chiringuito, kolem kterého jsem šel nekolikrát, takže jsem si tam poslední den zašel na oběd. Měl jsem grilované krevety a pak ještě sardinky a bylo to moc dobré. A domluvíte se tam i anglicky (já tedy mluvil španelsky, ale pak přišli nějací turisti a jeden číšník si to anglicky dával dost dobře).

Krevety v Merendero Casa Jose

  • Primark - naschvál jsem si sebou vezl méně triček a radši jsem si koupil čtyři jednobarevná bavlněná v Primarku po 2€ za kus.

  • Decathlon - stejně tak jsem si sebou nebral tepláky na doma a koupil jsem si je tady v Decathlonu. Plánoval jsem si totiž stejně pořídit nové a takhle je povezu jen jednu cestu. Jo a funguje tu Decathlon kartička z českého Decathlonu (díky Portmonka).

  • Informační centrum v centru - doporučuji si dojít pro mapu, kde jsou vyznačené různé památky, případně si nechat něco doporučit. Ale s Google Maps si klidně vystačíte.

Zajímavosti

  • od 22 h do 8 h se nesmí prodávat alkohol jinde než v restauracích a barech. Takže by vám večerce neměli prodat láhev tvrdého. Neměli by.

  • Pokud budete v Málaze poprvé, tak můžete vyzkoušet nějakou free guided tour. Obecně to funguje tak, že si nějakou najdete, zúčastníte se a pak jim něco zaplatíte podle toho, jak jste byli spokojení. (nemám vyzkoušeno)

  • Jedno odpoledne se ze slunečného dne během chvíle udělala hustá mlha. Hledal jsem a říká se tomu mlha Taró.

  • Výběry z většiny bankomatů stojí kolem 2€ navíc se zahraniční kartou (i s Revolut kartou). Ale kupodivu Unicaja je má (zatím) zdarma, takže hledejte tu.

]]>
<![CDATA[Keep your YAML files sorted with YAML sort checker]]> 2018-01-17T00:00:00+00:00 https://blog.martinhujer.cz/yaml-sort-checker/ Last year I've created a PHP tool YAML file sort checker that checks that YAML files in project are sorted alphabetically.

Why should you sort everything alphabetically?

You can prevent unnecessary merge conflicts if you keep everything sorted. You should sort not only YAML files, but also composer.json (require, require-dev and scripts sections), use in PHP classes and any PHP array where order is not significant.

Typical situation is when two developers register a new service in services.yml. If they both add it to the end, it will inevitably lead to a merge conflict. However, when the services are alphabetically sorted, the probability of merge conflict is much lower, because the services will be probably added to different places and therefore won't clash.

(This specific example got better with new DI improvements in recent Symfony versions, where you don't need to register that many services manually)

Tip: You can use AlphabeticallySortedUses sniff from Slevomat Coding Standard to check that use in PHP class are sorted.

Installation

YAML file sort checker is installed through Composer:

composer require --dev mhujer/yaml-sort-checker

Configuration

You have to create a configuration file yaml-sort-checker.yml in the project root directory. Then list all files that should be checked in it.

The initial config may look like this:

files:
    app/config/services.yml:
        depth: 2

    yaml-sort-checker.yml:

It checks services.yml and itself. The services.yml will be validated only two levels deep.

Sometimes you may want to exclude some keys from validation. It can be accomplished by using excludedKeys option. You can see how the exclusion works in the example bellow:

files:
    app/config/config_prod.yml:
        excludedKeys:
            monolog:
                handlers:
                    main:
                        - type
                    nested:
                        - type

    app/config/config_test.yml:
        excludedKeys:
            0: imports

Run the checker

After you have created a configuration file, you can run the checker:

vendor/bin/yaml-sort-checker

PHPStorm integration

It is possible to integrate YAML sort checker with PHPStorm using File Watchers. It is described in the project README on github so I won't duplicated it here.

Conclusion

It is a good practice to keep most of the things sorted alphabetically to prevent merge conflicts. However, it can be a tedious task to do manually. Luckilly the YAML files can be automatically checked with YAML file sort checker.

]]>
<![CDATA[Have you tried Composer Scripts? You may not need Phing.]]> 2018-01-14T00:00:00+00:00 https://blog.martinhujer.cz/have-you-tried-composer-scripts/ Phing is a great tool (I'm using it as well), but in this article, I want to show you that some projects may not need it. Composer contains a powerful feature called "Scripts", which can be used to create a simple build script.

If you haven't read it yet, I suggest that you read my article 17 Tips for Using Composer Efficiently before reading this one.

Creating a build script for launching PHP_CodeSniffer

Let's say that you have installed PHP_CodeSniffer and you run it with this command:

vendor/bin/phpcs --standard=PSR2 src

You probably want your colleagues and CI server to run it with the same parameters. To do so, you need to store it somewhere. You can either create a build.xml for Phing or put it in a bash script (and batch file to cover Windows). Or you can leverage the power of Composer scripts.

Put the command to the composer.json file with all its parameters:

  "scripts": {
      "phpcs": "phpcs --standard=PSR2 src"
  }

And run it this way:

# either explicitly:
composer run-script phpcs

# or via shortcut:
composer phpcs

Tip: Use alias for running Composer

If you are launching Composer by typing composer (or even php composer.phar) every time, you may want to save time by creating an alias for it.

On Windows, you need to create a .cmd file in a directory which is in the system PATH. I've created c:\dev\php\x.cmd with this content (%* passes through all the parameters):

php c:\devweb\php\composer.phar %*

On Linux, you can add an alias to ~/.bashrc:

alias x="composer"

Now you can run Composer just by typing x and a command (e.g. x phpcs).

Tip: Don't type whole Composer command name

Because Composer CLI is powered by Symfony Console, you can save some characters on unambiguous commands. Instead of writing composer update, it is enough to write composer up (or only x up if you also applied the previous tip)

Creating more complex build script

Let's start with an example:

  "scripts": {
      "ci": [
          "@phpcs",
          "@test"
      ],
      "phpcs": "phpcs --standard=PSR2 src",
      "test": "phpunit"
  }

I've added a new script called test which just launches PHPUnit with default configuration.

The ci script is more interesting. It is not an actual script, but a meta-script that references several other scripts. The referenced scripts are prefixed by @. This way, you can create more complex scripts without duplication.

Launching Composer or PHP from scripts

You can use the @composer and @php commands to launch the same Composer or PHP executable that is running the script.

For example you may want to validate the composer.json file during CI build:

  "scripts": {
      "ci": [
          "@composer validate --no-check-all --strict",
          ...
      ]
  }

Or you want to use YAML validation that is available as a Symfony Console command:

"scripts": {
    "yamllint": "@php bin/console lint:yaml app"
}

Don't forget to document the custom scripts

You can use the scripts-descriptions section to document what custom scripts do:

"scripts-descriptions": {
    "phpcs": "Checks that the application code conforms to coding standard",
    "test": "Launches the preconfigured PHPUnit"
}

Configure timeout for long-running scripts

If you have some long-running scripts, you should configure the process timeout. It defaults to 300 which means that Composer will terminate the script after 300s. You can either set a specific time limit in seconds, or 0 for unlimited.

Timeout can be configured in the ENV variable COMPOSER_PROCESS_TIMEOUT:

export COMPOSER_PROCESS_TIMEOUT=600

Or by adding --timeout=0 argument when running the script:

composer phpunit --timeout=3600

Or in config section of composer.json:

"config": {
    "process-timeout": 0
}

Tips for the Scripts:

  1. You can use composer run-script --list to list custom scripts.

  2. Be careful not to create a script with a name conflicting with the existing Composer command. Composer throws a warning on every run when such a script is present in composer.json.

  3. You don't have to update composer.lock when adding or changing the scripts, because they are not included in composer.lock at all.

  4. You can even call PHP callbacks from scripts (Static methods in classes autoloadable by Composer). But I don't recommend using them for build scripts because potential migration to other build system would be hard.

When should I switch to Phing?

Composer Scripts are great for simple build scripts. But it is important to recognize the moment when the build script is so complex, that a dedicated build tool would do better.

As a rule of thumb, you shouldn't do any files / directories manipulation in Composer scripts (as it would be hard to do it Linux/Windows compatible) and switch to Phing instead.

Conclusion

Composer scripts are a lightweight tool to create build scripts. However, it is important to know when to switch to a dedicated tool such as Phing.

Do you like them?

]]>
<![CDATA[17 Tips for Using Composer Efficiently]]> 2018-01-05T00:00:00+00:00 https://blog.martinhujer.cz/17-tips-for-using-composer-efficiently/ Although most PHP developers know how to use Composer, not all of them are using it efficiently or in a best possible way. So I decided to summarize things which are important for my everyday workflow.

The philosophy of most of the tips is "Play it safe", which means that if there are more ways how to handle something, I would use the approach which is least error-prone.

Tip #1: Read the documentation

I really mean it. The documentation is great and a few hours of reading it will save you more time in the long run. You would be surprised how many things Composer can do.

Tip #2: Be aware of differences between a "project" and a "library"

It's important to know, whether you are creating a "project" or a "library". Each of them requires separate set of practices.

A library is a reusable package, that you would add as a dependency - such as symfony/symfony, doctrine/orm or elasticsearch/elasticsearch.

A project is typically an application, that depends on several libraries. It is usually not reusable (no other projects would require it as a dependency). Typical example is an ecommerce website, customer support system etc.

I will distinguish between library and a project in the tips bellow.

Tip #3: Use specific dependencies' versions for applications

If you are creating an application, you should use the most specific version to define the dependency. If you need to parse YAML files, you should specify the dependency like this "symfony/yaml": "4.0.2".

Even if the library follows Semantic Versioning, there may be backwards-compatibility breaks in the minor and patch versions. For example, if you are using "symfony/symfony": "^3.1", there may be something deprecated in 3.2 which may break your application tests. Or there may be a bug fixed in PHP_CodeSniffer and it would detect new formatting issues in your code, which again may lead to a broken build.

The update of dependencies should be deliberate, not accidental. One of the tips bellow discusses it in greater detail.

It may sound as an overkill, but it will prevent your co-workers from accidentally updating all dependencies when adding a new library to project (which you may miss during Code Review).

Tip #4: Use version ranges for libraries dependencies

If you are creating a library, you should define the broadest version range possible. If you create a library that uses symfony/yaml library for YAML parsing, you should require it like this:

"symfony/yaml": "^3.0 || ^4.0"

It means that your library can utilize symfony/yaml from any Symfony 3.x or 4.x versions. It is important, because this constraint is passed to the application that uses your library.

In case there are two libraries with conflicting requirements, e.g. one requires ~3.1.0 and other requires ~3.2.0, the installation would fail.

Tip #5: You should commit composer.lock to git in applications

If you are creating a project, you definitely want to commit composer.lock to git. This ensures that everyone - you, your co-workers, your CI server and your production server - is running the application with the same dependencies versions.

At first glance, it may sound superfluous - you are already using a specific version in the constraint, as mentioned in the tip #3. But no, there are also the dependencies of your dependencies which are not bound by these constraints (e.g. symfony/console depends on symfony/polyfill-mbstring). So without committing the composer.lock, you won't get the exact same set of dependencies.

Tip #6: Put composer.lock into .gitignore in libraries

If you are creating a library (let's call it acme/my-library), you should not commit a composer.lock file. It does not have any effect on the projects that are using your library.

Imagine that the acme/my-library uses monolog/monolog as a dependency. If you have committed a composer.lock, everyone who is developing the acme/my-library would be using an older version of Monolog. But when the library is finished, and you use it in a real project, a newer version of Monolog may be installed, and it may not be compatible with the library. But you didn't notice it before, because of the composer.lock!

It is best to put composer.lock into your .gitignore so you won't commit it accidentally.

If you want to make sure that the library is compatible with different versions of its dependencies, read the next tip!

Tip #7: Run Travis CI builds with different versions of dependencies

This tip applies only to libraries (because you use specific versions for applications).

If you are building an open-source library, you are probably using Travis CI for running its builds.

By default, Composer installs the latest possible versions of dependencies which are allowed by the constraints in composer.json. It means that for the dependency constraint ^3.0 || ^4.0, the build would always use the latest version of the v4 release. As the 3.0 is never tested, the library may not be compatible with it and that would make your users sad.

Luckily, Composer provides a switch to install the lowest possible versions of dependencies --prefer-lowest (should be used with --prefer-stable to prevent installation of non-stable versions).

Updated .travis.yml configuration may look like this:

language: php

php:
  - 7.1
  - 7.2

env:
  matrix:
    - PREFER_LOWEST="--prefer-lowest --prefer-stable"
    - PREFER_LOWEST=""

before_script:
  - composer update $PREFER_LOWEST

script:
  - composer ci

See it live in my mhujer/fio-api-php library and the build matrix on Travis CI

Even though this solution would catch most of the incompatibilities, remember that there are many combinations of dependencies between lowest and latest versions. And they may be incompatible together.

Tip #8: Sort packages in require and require-dev by name

It is a good practice to keep packages in require and require-dev sorted by name. It can prevent unnecessary merge conflicts when rebasing a branch. Because if you have added a package to the end of the list in two branches, there would be a merge conflict every time.

It is a tedious task to do manually, so it is best to configure it in composer.json:

{
...
    "config": {
        "sort-packages": true
    },
…
}

Next time, you require a new package, it will be added to a proper place (and not to the end).

Tip #9: Do not attempt to merge composer.lock when rebasing or merging

If you add a new dependency to composer.json (and composer.lock) and before your branch is merged, there is another dependency added in master, you need to rebase your branch. And you will get a merge-conflict in composer.lock.

You should never try to resolve this conflict manually, because the composer.lock file contains a hash of dependencies defined in composer.json. So even if you resolve the conflict, the resulting lock file would be incorrect.

Best thing to do is to create .gitattributes in the project root with the following line, which means that the git won't even attempt to merge the composer.lock:

/composer.lock -merge

You can remedy this issue by using short-lived feature branches as suggested in Trunk Based Development (you should be doing this anyway). When you have a short-lived branch, which is merged promptly, the risk of merge conflict in composer.lock is minimal. You may even create a branch just for adding a dependency and merge it right away.

But what to do, if you encounter a merge conflict in composer.lock when rebasing? Resolve it with the version from master, so you will have changes only in composer.json (the newly added package). And then run composer update --lock, which will to update the composer.lock file with changes from composer.json. Now you can stage the updated composer.lock and continue with the rebase.

Tip #10: Know the difference between require and require-dev

It is important to be aware of the difference between require and require-dev blocks.

Packages which are required to run the application or library should be defined in require (e.g. Symfony, Doctrine, Twig, Guzzle, …). If you are creating a library, be careful about what you put to require. Because each dependency from this section is also a dependency of the application, which uses the library.

Packages necessary for developing the application (or library) should be defined in require-dev (e.g. PHPUnit, PHP_CodeSniffer, PHPStan).

Tip #11: Update dependencies safely

I guess we can agree on the fact that dependencies should be updated regularly. What I want to discuss here is that dependencies updating should be explicit and deliberate, not done just by-the-way with some other work. If you refactor something and at the same time update some library, you can't easily tell if the app was broken by the refactoring or by the update.

You can use composer outdated command to see what dependencies can be updated. It is a good idea to include --direct (or -D) switch to list only dependencies specified in composer.json. There is also a -m switch to list only minor version updates.

For each outdated dependency follow these steps:

  1. Create a new branch
  2. Update the dependency version in composer.json to the latest one
  3. Run composer update phpunit/phpunit --with-dependencies (replace phpunit/phpunit with the library you are updating)
  4. Check the CHANGELOG in the library repository on Github to see if there are any breaking changes. If so, update the application
  5. Test the application locally (If you are using Symfony, you can find deprecation warnings in the Debug Bar)
  6. Commit the changes (composer.json, composer.lock and anything else what was necessary for new version to work)
  7. Wait for the CI build to finish
  8. Merge and deploy

Sometimes it makes sense to update more dependencies at once, e.g. when you are updating Doctrine or Symfony. In this case you can list them all in update command:

composer update symfony/symfony symfony/monolog-bundle --with-dependencies

Or you can use a wildcard to update all dependencies from a specific namespace:

composer update symfony/* --with-dependencies

I know that this all sounds tedious, but you will probably update dependencies just occasionally, so it is worth the extra safety.

One shortcut which is acceptable to make is to update all require-dev dependencies at once (if they do not require changes in the code, otherwise I would suggest using separate branches for easier code review).

Tip #12: You can define other types of dependencies in composer.json

Apart from defining libraries as dependencies, you can also define other things there.

You can define, which PHP versions your application/library supports:

"require": {
    "php": "7.1.* || 7.2.*",
},

You can also define which extensions are required for the application/library. It is super-useful when you are trying to dockerize your application or your new colleague is setting-up the application for the first time.

"require": {
    "ext-mbstring": "*",
    "ext-pdo_mysql": "*",
},

(You should use * for the extensions version as they may be a bit inconsistent).

Tip #13: Validate the composer.json during the CI build

composer.json and composer.lock should be always kept in sync. Therefore, it is a good idea to have an automatic check for it. Just add this as a part of you build script and it will ensure that composer.lock is in sync with composer.json:

composer validate --no-check-all --strict

Tip #14: Use a Composer plugin in PHPStorm

There is a composer.json plugin for PHPStorm. It adds autocompletion and some validations when changing composer.json manually.

If you are using other IDE (or just a code editor), you can setup validation against its JSON schema.

Tip #15: Specify the production PHP version in composer.json

If you are like me and you are sometimes running pre-released PHP versions locally, you are in risk of updating the dependencies to a version that won't work in production. Right now, I'm using PHP 7.2.0 which means, that I can install libraries, that would not work on 7.1. As the production is running 7.1, the installation would fail there.

But no need to worry, there is an easy way out. Just specify the production PHP version in config section of composer.json:

"config": {
    "platform": {
        "php": "7.1"
    }
}

Don't confuse it with require section, which behaves differently. Your application may be able to run on 7.1 or 7.2 and at the same time specify 7.1 as a platform (which means that the dependencies will be always updated to a version compatible with 7.1):

"require": {
    "php": "7.1.* || 7.2.*"
},
"config": {
    "platform": {
        "php": "7.1"
    }
},

Tip #16: Using private packages from self-hosted Gitlab

It is recommended to use vcs as a repository type and the Composer should determine the proper way of fetching the packages. For example, if you are adding a fork from Github, it would use its API to download the .zip file instead of cloning the whole repository.

But it is more complicated for a private Gitlab installation. If you use vcs as a repository type, Composer will detect that it is a Gitlab installation would try to download the package using the API (which requires an API key. I didn't want to set it up, so I settled for this setup (which uses SSH for cloning):

First specify the repository with the type git:

"repositories": [
    {
        "type": "git",
        "url": "git@gitlab.mycompany.cz:package-namespace/package-name.git"
    }
]

Then use the package as you would have normally:

"require": {
    "package-namespace/package-name": "1.0.0"
}

Tip #17: How to temporarily use a branch with bugfix from fork

If you find a bug in some public library and you fix it in your fork on Github, you need to install the library from this repository instead of the official one (until the bugfix is merged and fixed version is released).

It can be done easily with inline aliasing:

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/you/monolog"
        }
    ],
    "require": {
        "symfony/monolog-bundle": "2.0",
        "monolog/monolog": "dev-bugfix as 1.0.x-dev"
    }
}

You can test the bugfix locally before pushing it by using path as a repository type.

Update 2018-01-08:

After publishing the article, I got suggestions for several more tips. So here they are:

Tip #18: Install prestissimo to speed up package installation

There is a Composer plugin hirak/prestissimo which speeds up dependencies installation by downloading them in parallel.

And the best thing? You only need to install it once, globally and it will work automatically for all projects:

composer global require hirak/prestissimo

Tip #19: Test your version constraints if you are not sure

Writing correct version constraints may sometimes be tricky even after reading the documentation.

Luckily, there is a Packagist Semver Checker where you can check which versions match the specified constraint. Instead of only analysing the version constraint, it downloads the data from Packagist to display the actual released versions.

See the result for symfony/symfony:^3.1.

Tip #20: Use authoritative class map in production

You should generate authoritative class map in production. It will speed-up class loading by including everything in class-map and skipping any filesystem checks.

You can do it by running this as a part of your production build:

composer dump-autoload --classmap-authoritative

Tip #21: Configure autoload-dev for tests

You don't want to include test files in production class map (because of the file size and memory). It can be done by configuring the autoload-dev (similarly to autoload):

"autoload": {
    "psr-4": {
        "Acme\\": "src/"
    }
},
"autoload-dev": {
    "psr-4": {
        "Acme\\": "tests/"
    }
},

Tip #22: Try Composer scripts

Composer scripts are a lightweight tool to create build scripts. I have written a separate article about them.

Conclusion

If you disagree with some of the tips, I would be happy if you can describe why in the comments (don't forget to put the tip number there).

]]>
<![CDATA[Madrid - co navštívit?]]> 2017-11-25T00:00:00+00:00 https://blog.martinhujer.cz/madrid/ S Ivčou jsme strávili jeden listopadový týden v Madridu. Centrum jsme prochodili křížem krážem a tady bych chtěl shrnout zajímavosti, které byste určitě neměli minout (a nebo naopak měli).

Palacio Real de Madrid

Bydlení

Bydleli jsme vedle parku El Retiro, takže to do centra byl kousíček.

Co navštívit

(seznam není nijak seřazený)

Ve všední dny mají některá muzea a galerie navečer vstup zdarma (doporučuji čas vždycky ověřit na jejich webu).

  • Parque de El Retiro - obrovský park v centru města. Najdete v něm i růžovou zahradu nebo pávy.

  • Estanque grande del Retiro - ještě chvíli zůstaneme v El Retiro, protože v něm je taky jezírko, kde si jde půjčit lodičku (Myslím, že za 6€). Pokud bude hezké počasí, tak to určitě stojí za to. A doporučuji všední den, o víkendu tam byla dost fronta.

  • Atocha Cercanías - vlakové nádraží, které uvnitř obsahuje botanickou zahradu. Včetně jezírka se spoustou želviček.

  • Real Jardín Botánico - botanická zahrada, kde toho bylo dost k vidění i v listopadu. Mají tam výstavku bonsají. A super skleníky. Vstup 4€.

  • Casa Museo Lope de Vega - muzeum Lope de Vega, mají prohlídku zdarma, ale je potřeba se dopředu objednat e-mailem. A prohlídky umí i v angličtině ;-)

  • El Rastro - trh každou neděli dopoledne. Původně to prý byl blešák, ale mně to přišlo spíš jako obyčejná tržnice.

  • Chocolatería San Ginés - prodávají churros už od roku 1894. Zajímavostí je, že mají otevřeno 24 hodin denně. Ať vás nepřekvapí, že v rámci efektivity se neobjednává u stolu, ale nejdříve vystojíte frontu a objednáte si u baru. Teprve pak si sednete a nějaký číšník si od vás vezme lísteček s objednávkou.

    Churros

  • Mercado de San Miguel - street food market s různými tradičními španělskými jídly (různé rybí pochoutky, croquetas, jamón, olivy, apod.). Nás víc bavil Mercado San Anton, kde bylo méně lidí.

  • Museo Nacional del Prado - do Prada jsme nakonec vůbec nešli, protože nás obrazy moc nebaví. Ke konci dne mají vstup zdarma a to tam je prý dost narváno.

  • Museo Naval de Madrid - muzeum španělského námořnictví. A že mají co ukazovat! Bylo to parádní a vstupné jen 3€.

  • Museo de Historia de Madrid - muzeum historie Madridu. Vstup zdarma. A popisky i v angličtině.

  • Teleférico de Madrid - městská lanovka, odveze vás do Caso de Campo. Byla tam docela fronta, ale zážitek pěkný.

  • Caso de Campo - sem vás doveze lanovka. Je to obrovský park nebo možná spíš "les". Místní tam jezdí na pikniky. Zpátky můžete zas jet lanovou nebo klidně dojít pěšky.

  • Primark - čtyřpatrový Primark je epic. Ale nakonec jsme nic nekoupili, protože tam bylo hrozně moc lidí a dlouhé fronty na kabinky.

  • Catedral de la Almudena - hezká katedrála

  • Campo del Moro - velký park za královským palácem. Pozor, má to jen jeden vstup.

  • CentroCentro - kulturní centrum. Mají tam nějaké haluzné výstavy a něco jako coworking (wifi, stoly, zásuvky). A hlavně se za 2€ dá jít na vyhlídkovou věž.

  • Madrid Río - obrovský park podél řeky. Kromě jiného v něm mají i skluzavky a houpačky pro dospělé!

  • Matadero Madrid - původně jatka, udělali z toho kulturní centrum. Zajímavá architektura.

  • Templo de Debod - starý egyptský chrám, je o něj pěkný výhled na západ slunce

    Templo de Debod

  • Rosaleda del Parque del Oeste - zahrada s růžemi

  • Parque del Oeste - v jedné části parku je několik bunkrů z občanské války - jsou vyznačené na Mapy.cz

  • El Huerto de las Monjas (tras El Pasaje De Edificios) - schovaný minipark v centru města

  • Indalo Tapas Atocha - nevím jak vy, ale my máme problém si vybrat, do jaké hospody apod zajít. Tohle je jedna, kde jsme byli na tapas a bylo to fajn. Takže pokud budete okolo, tak tomu můžete dát šanci.

  • La Panetteria - kavárna na náměstí Tirso de Molina. Byli jsme tam za ten týden dokonce 2×.

Praktické

  • Metro - druhý nejlepší způsob dopravy po centru (nejlepší je chodit pěšky). Je potřeba si za 2€ koupit čipovou kartu a do ní potom třeba balíček deseti jízd. Můžete je klidně používat ve více lidech, prostě si u turniketu pípnete postupně oba. A pozor, na cestu z (a na) letiště si k běžné jízdence ještě musíte přikoupit Airport-pass za 3€. Na plánování trasy doporučuji používat Google Maps, fungují dobře.

  • Pohledy a známky - pokud budete posílat pohledy, tak pozor, ať si koupíte správné známky. Správné jsou Correos, které koupíte třeba na poště. Ty se hází do žlutých poštovních schránek, kterých je docela dost. Ale v některých stáncích si můžete omylem koupit známky od něčeho, co se jmenuje "City Post". A ty musíte hodit do nějaké speciální červené schránky, která skoro nikde není. No a pokud si nevšimnete, že nemáte standardní známky a pohled hodíte do běžné poštovní schránky, tak nedorazí.

  • Wi-fi - po městě je spoustu free wi-fi. Většina chce přihlášení přes Facebook, takže doporučuji si na to založit nějaký druhý účet (protože typicky to chce práva na sdílení příspěvků pod vámi).

OutdoorVisit outdoor tours

Pokud chcete zkusit něco netradičního, tak doporučuji mrknout na outdoorové zážitky a výlety v Madridu a okolí.

]]>
<![CDATA[JavaScript Error Handler Bundle for Symfony]]> 2017-11-04T00:00:00+00:00 https://blog.martinhujer.cz/javascript-error-handler-bundle/ I've created Symfony JavaScriptErrorHandlerBundle for visualizing accidental errors in JavaScript.

Why?

It is easy to break JavaScript code by an unrelated change. If you have JavaScript-only app, you will notice it immediately, because the app won't render at all (and your framework would report the error somehow).

On the other hand, if you are creating more traditional application with HTML rendered on the server and some interactions enhanced by jQuery, you may not notice that you've broken it. The browser reports the error into the Developer Console, but if you don't have it open, you are out of luck.

My JavaScriptErrorHandlerBundle listens to window.onerror events and converts them to alerts, which are hard to miss.

example alert with error

How does it work?

The Bundle works automatically with zero configuration. You only have to install it through Composer and register in AppKernel.php (see README for instructions).

It registers an event listener, which injects JavaScript snippet with error into the <head> section of the response (similarly to Symfony WebDebugToolbar). By default, the listener is registered only when the %kernel.debug% is set to true, so the error alert won't be ever displayed to your end users. The configuration cab be changed if necessary - e.g. you may want to have enabled also at the staging server.

Conclusion

I know that the bundle is very simple. I wanted to experiment with what it takes to create a proper Bundle, before I work on something more complicated. One of the challenging things was the automatic testing of the container extension (it contains the logic for registering the listener). Luckily, there is SymfonyDependencyInjectionTest library created by Matthias Noback which makes container extension testing a breeze.

If you have time to dive into the bundle code, can you spot something that can I improve?

]]>
<![CDATA[PHP 7.2 is due in November. What's new?]]> 2017-09-07T00:00:00+00:00 https://blog.martinhujer.cz/php-7-2-is-due-in-november-whats-new/

I've published the Czech translation of the article on Zdroják.cz.

PHP 7.2 is planned to be released on 30th November 2017 (see the timetable). And it comes with two new security features in the core, several smaller improvements and some language legacy clean-ups. In the article, I will describe what the improvements and changes are. I read the RFCs, discussions on internals and PRs on Github, so you don't have to.

I'll update the article if there are any significant changes in PHP 7.2 until release.

You can try the pre-release version right now. Just download it using the links in the PHP 7.2.0 Release Candidate 1 Released. I've been using it locally for development since the 7.2.0-beta1 without issues.

password_hash function has been available in PHP since 5.5. It was designed to be able to utilize different password hashing algorithms. But back then only PASSWORD_BCRYPT was available.

IMPORTANT: If you are using sha1() or md5() for password hashing, please stop reading immediately and go fix your systems to use password_hash(). I recommend reading Michal's article Upgrading existing password hashes.

PHP 7.2 adds an option to use Argon2i algorithm with PASSWORD_ARGON2I constant:

password_hash('password', PASSWORD_ARGON2I)

Using PASSWORD_BCRYPT is still perfectly valid and safe option. Argon2i is just another option, which may become a default in the future.

Argon2i supports providing custom cost factors (like providing single cost factor to bcrypt):

$options = [
    'memory_cost' => PASSWORD_ARGON2_DEFAULT_MEMORY_COST,
    'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
    'threads' => PASSWORD_ARGON2_DEFAULT_THREADS,
];
password_hash('password', PASSWORD_ARGON2I, $options);

You should determine appropriate costs for Argon2i by experimenting on the target server (like you do with bcrypt cost).

It is also time to check your password column length! PASSWORD_BCRYPT generates 60 characters long hashes. PASSWORD_ARGON2I hash is 96 characters long. The password_hash documentation recommends columns length of 255 characters to accommodate any future hash (especially when using PASSWORD_DEFAULT as an algorithm):

It is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).

There is a good article about Protecting passwords with Argon2 in PHP 7.2 on Zend Framework blog.

Note: Argon2i is available only if the PHP was compiled with --with-password-argon2 flag. (It is included in Windows binaries available from php.net).

PHP 7.2 comes with cryptography library libsodium in the core. It was previously available through PECL. There is also a polyfill available so you can start using it right now (it even supports PHP 5.2.4!).

I don't know much about the cryptography, but the single most important thing I know is this: Don't invent your own cryptography! (See this StackExchange answer for reasoning.).

So just use the sodium_ functions instead of implementing it by yourself.

I recommend reading two following articles about upcoming improvements in PHP cryptography (both written by Scott Arciszewski, the author of the Libsodium RFC):

  1. PHP 7.2: The First Programming Language to Add Modern Cryptography to its Standard Library

    When version 7.2 releases at the end of the year, PHP will be the first programming language to adopt modern cryptography in its standard library.

  2. It Turns Out, 2017 is the Year of Simply Secure PHP Cryptography (by Scott Arciszewski as well).

Other Interesting Changes

It will be possible to use object as parameter type and as a return type.

<?php declare(strict_types = 1);

function foo(object $definitelyAnObject): object
{
    return 'another string';
}

foo('what about some string?');
// TypeError: Argument 1 passed to foo() must be an object, string given, called in...


foo(new \stdClass());
// TypeError: Return value of foo() must be an object, string returned

object becomes a keyword in 7.2, so make sure you are not using it as a name for a class, interface or trait. It was a soft-reserved word since PHP 7.0.

It is useful mainly for libraries (ORMs, DI containers, serializers, and others). But it may help clean up some of your code as well. In our codebase, it will allow us to improve the helper method used in tests:

class TestUtils
{

    /**
     * @param object $object
     * @param string $propertyName
     * @param mixed $value
     */
    public static function setPrivateProperty($object, string $property, $value): void
    {
        if (!is_object($object)) {
            throw new \Exception('An object must be passed');
        }

Will be simplified to this:

class TestUtils
{
    public static function setPrivateProperty(object $object, string $property, $value): void
    {

With this change, it is possible to omit the parameter type declaration in the child classes. I know that may sound confusing, so let's have a look at the example:

<?php declare(strict_types = 1);

class LibraryClass 
{
    public function doWork(string $input)
    {

    }
}

class UserClass extends LibraryClass
{
    public function doWork($input)
    {

    }
}

// PHP <7.2.0
// Warning: Declaration of UserClass::doWork($input) should be compatible with LibraryClass::doWork(string $input) in …

// PHP 7.2.0+
// no errors

It would allow libraries to introduce scalar typehints without breaking subclasses created by the library users. But it is rather theoretical possibility because the library cannot do the same for the return types. And when the library is adding parameter typehints, it makes sense to add return types as well.

Omitting type hints in subclasses follows the Liskov substitution principle - LSP because it widens the accepted types. But the subclass cannot omit return typehints, because that would widen the possible return types (and that is LSP violation).

This behaviour was also changed for abstract classes in separate RFC: RFC: Allow abstract function override.

Did you know, that it is possible to count scalar values? Well, it does not really do much counting, it only returns 1 for scalars.

<?php

var_dump(count(null)); // int(0)
var_dump(count(0)); // int(1)
var_dump(count(4)); // int(1)
var_dump(count('4')); // int(1)

Since PHP 7.2, it will begin emitting a warning:

Warning: count(): Parameter must be an array or an object that implements Countable in /in/4aIl2 on line 3

I really like this change, because no one writes such code intentionally, but rather as a bug that needs fixing.

I guess that you may find some of those is your codebase as well - have a look at a less obvious example:

<?php declare(strict_types = 1);

class Data
{
    /** @var string[] */
    private $data;

    public function addOne(string $item)
    {
        if (count($this->data) >= 5) {
            throw new \Exception('too much data');
        }

        $this->data[] = $item;
    }
}

$data = new Data();
$data->addOne('item1');
$data->addOne('item2');
$data->addOne('item3');

// Warning: count(): Parameter must be an array or an object that implements Countable in ...

It will report a warning, because you are counting a null value in the if.

Can be also called "Nikita Popov's language clean-up" :-)

The following will throw a deprecation warning in PHP 7.2 and will be removed later (probably in 8.0). See the RFC for detailed explanation of each one:

  • __autoload() - use spl_autoload_register() instead
  • $php_errormsg - use error_get_last() instead
  • create_function() - use closures instead
  • mbstring.func_overload (ini setting) - use mb_* functions directly
  • (unset) cast - this is not deprecating unset($var) but $foo = (unset) $bar which is equal to $foo = null (yes, strange one)
  • parse_str() without second argument - directly creating variables when parsing query string is not something you should be doing (register_globals anyone?)
  • gmp_random() - use gmp_random_bits() and gmp_random_range() instead
  • each() - use foreach instead (it is more than 10 times faster)
  • assert() with string argument - it is using eval() internally!
  • $errcontext argument of error handler - use debug_backtrace() instead

Smaller changes

Did you know, that get_class() without parameters returns the same value as __CLASS__? I didn't. But the RFC does not change this.

<?php

class MyClass
{
    public function myInfo()
    {
        var_dump(get_class()); // string(7) "MyClass"
        var_dump(__CLASS__); // string(7) "MyClass"
    }
}

$a = new MyClass();
$a->myInfo();

var_dump(get_class($a)); // string(7) "MyClass"

The RFC deprecates calling it with null as parameter. Consider this example from the RFC. If the item is fetched from the repository and $result is not null, it will print the class name of the fetched object. But if it is null, Foo will be printed.

class Foo
{

    function bar($repository)
    {
        $result = $repository->find(100);

        echo get_class($result);
    }
}

// In 7.2: Warning: get_class() expects parameter 1 to be object, null given in ...

If you configured the PHP extensions before 7.2, you had to use full filename in the .ini file:

On Windows:

extension=php_mbstring.dll

On Linux:

extension=mbstring.so

That's fine if you are using only one platform. But it gets a bit harder, if you need to write some automation scripts supporting multiple platforms. It was improved in 7.2 and now you can use this both on Linux and Windows:

extension=mbstring

Legacy syntax is still supported, but it is recommended to use the new one.

This is another step in making PHP safer to use. If you mistype a constant in 7.2, it will throw a warning instead of notice (and will throw an error in PHP 8.0).

Simplest example is this:

<?php
var_dump(NONEXISTENT_CONSTANT);


// PHP 7.1
// Notice: Use of undefined constant NONEXISTENT_CONSTANT - assumed 'NONEXISTENT_CONSTANT' in …

// PHP 7.2
// Warning: Use of undefined constant NONEXISTENT_CONSTANT - assumed 'NONEXISTENT_CONSTANT' (this will throw an Error in a future version of PHP) in …

I really like the bug examples in the RFC showing what it's aiming to prevent:

$foo = flase; // typo!
// ...
if ( $foo ) {
   var_dump($foo); // string(5) "flase"
}

And:

$found = false;
foreach ( $list as $item ) {
   if ( is_null($item) ) {
       contniue; // this statement issues a notice and does nothing
   }
   // lines assuming $item is not null
}

It has always been possible to use trailing comma after last array item:

$foo = [
    'foo',
    'bar',
];

It is useful for creating clean diffs in VCS and makes adding new items to the list simple.

This RFC proposed adding an optional trailing comma to all list-like places:

  • Grouped namespaces
  • Function/method arguments (declarations & calls)
  • Interface implementations on a class
  • Trait implementations on a class
  • Class member lists
  • Inheriting variables from the parent scope in anonymous functions

I especially hoped for method arguments declarations and calls. But it did not pass the vote (which needed 2/3 majority, as it is a language change).

Surprisingly, only the "Grouped namespaces" passed:

use Foo\Bar\{ 
    Foo,
    Bar, 
    Baz, 
}; 

// note the comma after "Baz"

Conclusion

PHP 7.2 will contain new security features (sodium, Argon2i), several language improvements (object typehint, type widening) and a variety of minor changes that polish legacy parts of the language.

The RC1 was released on 31st August, so it is now a right time to try it out. If you can't or don't want to use it locally for development just yet, you should at least run your test suite against PHP 7.2 to see if there is anything to be fixed. I think there won't be many compatibility issues in a well-managed codebase.

If you are maintaining an open-source project, you should add 7.2 to TravisCI build matrix to ensure that your users won't encounter any compatibility issues. Or you can require PHP 7.2 in the next major version like Doctrine ORM will.

Thank you, Sara and Remi, and all the contributors for making this PHP release great!

What are you looking forward to most in the PHP 7.2?

]]>
<![CDATA[Don't Use Entities in Symfony Forms. Use Custom Data Objects Instead]]> 2017-08-23T00:00:00+00:00 https://blog.martinhujer.cz/symfony-forms-with-request-objects/

First part of this article explains why entities should not be used directly in Symfony Forms. Second part presents an approach which solves most of the problems presented in the first part.

Let's start with stating that using entities for validation in Symfony Forms is widely used and widely recommend approach. Even the official documentation suggests it.

And I don't think it is a good idea!

Why?

1. An entity should be always valid.

An entity should be always valid. It should not be possible for the entity to get to some inconsistent state. And that's exactly what the form validation is doing. When the form is submitted, the data are injected (through public properties or setters) into the entity and validated. And even if the validation fails, the invalid data are kept there and you have an invalid entity at hand.

Read those slides from Ocramius or watch the video for great explanation what is means to have a valid entity (and much more).

It's also not that hard to imagine the situation when this can cause you serious trouble. If the entity is already managed by EntityManager (because it is an updateAction) and there is a $entityManager->flush() call lurking in some listener, you'd end up with invalid data stored in database.

2. Change! Change! Change!

The only sure thing about the software development is "change". Eventually you will need to change the structure of the form, maybe split it in two-step form. And the form fields will no longer map exactly 1:1 to entity's fields.

3. Layers separation

It breaks the layers separation. Each layer should depend only on the deeper ones, not the other way around.


What can we do instead of using entities in forms? Symfony documentation describes how to use forms with data stored in array. It is a viable solution, but it has disadvantages as well. Some I can think of is that you won't get code completion in IDE for the form data. Or that it is hard to statically analyze arrays with tools such as PHPStan.

And there is another solution, the one I prefer.

Custom Data Classes for the win

To get around the disadvantages mentioned above, I suggest using a custom class to represent the form data.

Let's have a look at the example:

use Symfony\Component\Validator\Constraints as Assert;

class CreateArticleRequest
{

    /**
     * @Assert\NotBlank()
     * @Assert\Length(min="10", max="100")
     * @var string
     */
    public $title;

    /**
     * @Assert\NotBlank()
     * @var string
     */
    public $content;

    /**
     * @Assert\DateTime()
     * @var \DateTimeImmutable
     */
    public $publishDate;

}

It is a simple class that has some public properties and validator annotations. The main advantage is that is has nothing to do with the actual entity. CreateArticleRequest can handle as much of invalid data as you want and it won't cause you any trouble.

Second step is using the request object in the controller. You can use it the same way you'd use the entity (the following code should be self-explanatory):

  /**
     * @Route("/article/create/", name="article_create")
     */
    public function createAction(Request $request)
    {
        // create an instance of an empty CreateArticleRequest
        $createArticleRequest = new CreateArticleRequest();

        // create a form but with a request object instead of entity
        $form = $this->createForm(ArticleFormType::class, $createArticleRequest);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

            // ArticleFacade creates instance of an Article,
            // persists it and flushes the EntityManager.
            // (details are out of scope of this article)

            $article = $this->articleFacade->createArticle(
                $createArticleRequest->title,
                $createArticleRequest->content,
                $createArticleRequest->publishDate
            );

            // ... use $article to add title to flash message or something

            return $this->redirectToRoute('articles_list');
        }

        // render the form if it is the first request or if the validation failed
        return $this->render('article/add-article.html.twig', [
            'form' => $form->createView(),
        ]);
    }

And for the sake of completeness, the source of ArticleFormType:

class ArticleFormType extends \Symfony\Component\Form\AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('title', TextType::class, [
                'label' => 'Article title',
            ])
            ->add('content', TextareaType::class, [
                'label' => 'Article title',
            ])
            ->add('publishDate', DateTimeType::class, [
                'label' => 'Publish on',
            ])
            ->add('save', SubmitType::class, [
                'label' => 'Save',
            ]);
    }
}

I call the data class CreateArticleRequest because it is a Request to create an article. You will probably also have a UpdateArticleRequest class with different properties (in some cases, both classes may be the same, so one ArticleRequest would be sufficient).

The *Request suffix may cause some confusion with the Request class which represents a HTTP request. If that is your case, you are free to change the suffix to *Data and use a class called CreateArticleData.

What about update form?

One of the specifics of the update is that it won't necessarily have the exact same fields as the "create". In the example, we don't want to update the publishDate field in the entity. The UpdateArticleRequest will look like this:

use Symfony\Component\Validator\Constraints as Assert;

class UpdateArticleRequest
{

    /**
     * @Assert\NotBlank()
     * @Assert\Length(min="10", max="100")
     * @var string
     */
    public $title;

    /**
     * @Assert\NotBlank()
     * @var string
     */
    public $content;

    public static function fromArticle(Article $article): self
    {
        $articleRequest = new self();
        $articleRequest->title = $article->getTitle();
        $articleRequest->content = $article->getContent();

        return $articleRequest;
    }
}

You can see that the $publishDate field is missing, but more importantly, we have a new method there - fromArticle(Article $article). It allows you to prepopulate the data from the existing article.

Check the following example of updateAction() to see how to use it in controller:

    /**
     * @Route("/article/update/{id}/", name="article_update")
     */
    public function updateAction(Article $article, Request $request)
    {
        // the $article argument is converted from {id} by implicit ParamConverter

        // pre-populate the UpdateArticleRequest instance with the data from the article
        $updateArticleRequest = UpdateArticleRequest::fromArticle($article);

        $form = $this->createForm(UpdateArticleFormType::class, $updateArticleRequest);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

            // ArticleFacade updates instance of an Article and flushes the EntityManager.
            // (details are out of scope of this article)

            $this->articleFacade->updateArticle(
                $article,
                $updateArticleRequest->title,
                $updateArticleRequest->content
            );

            // ... use $article to add title to flash message or something

            return $this->redirectToRoute('articles_list');
        }

        return $this->render('article/edit-article.html.twig', [
            'form' => $form->createView(),
        ]);
    }

You may think - that's a lot of code to write! I agree, but rest assured it is worth it in the long run. If your app contains some business logic and is not just a plain CRUD, it will eventually need different fields and validation rules during create and update. Then you will make good use of this extra code you had written.

Conclusion

In the article, I suggested why it may not be the best idea to use entities in Symfony Forms. The second part of the article proposes a way how to tackle this problem - by using a custom object instead of entity to carry the data and handle the validation.

There are two more takeaways:

  1. Always separate the application layers.
  2. Do not blindly follow the documentation (or other developers).

Do you use similar solution in your projects? If you are using entities, have you already encountered any problems?

Finally, you may want to read two related articles: Avoiding Entities in Forms and Rethinking Form Development (written by Iltar van der Berg).

]]>