Vagrant + Windows static file caching

Oplossing voor het vreemdste probleem van vandaag; Ik heb een Vagrant box draaien in Virtualbox welke via een vm.share_folder een virtuele share maakt tussen bestanden op de pc en de virtuele server. Dit werkte voor dynamische bestanden prima maar bij o.a. CSS & JS files ging het fout.

Als ik het bestand opriep via de browser kreeg ik een oude versie (ongeacht of je er bijvoorbeeld .css?1 van maakte of iets). Gooide ik het bestand weg dan werkte het wel. Alles nagelopen rondom browser en server-side caching maar daar zat het niet.

De dader: Sendfile()

Na wat onderzoek op het grote web kwam ik erachter dat dit komt door de manier waarop statische files worden opgehaald middels de sendfile() call welke in Virtualbox echter niet goed werkt. Hierdoor werd het bestand als het ware éénmalig echt opgehaald en vanaf dat punt kreeg je een versie uit de ‘cache’ terug. De verwarring is groot omdat dit bestand zich niet als gecached gedraagt; de headers zijn goed, er zijn geen opvallende cache-tags te vinden of iets.

De oplossing

Als je op Windows zit is de meest handige oplossing om sendfile uit te schakelen. Dit doe je met de volgende config line voor je Apache;
EnableSendfile off

Voor Mac gebruikers zou ik eerder gaan kijken naar andere manier om de virtual share te maken namelijk NFS: http://docs-v1.vagrantup.com/v1/docs/nfs.html

Dit probleem speelt hier niet en de performance is aanzienlijk beter.

Zend Framework 2: Form validation error messages translated

Om mij nog niet geheel duidelijke redenen gebruikt ZF2 voor de vertalingen van validatie errors niet de default locale. Dit is erg spijtig maar is relatief eenvoudig op te lossen. Even de stappen bij elkaar om het helder te houden.

Translator configureren: autoload/global.php

'service_manager' => array(
    'aliases' => array(
        'translator' => 'MvcTranslator',
    ),
),
'translator' => array(
    'locale' => 'nl_NL',
    'translation_file_patterns' => array(
        array(
            'type' => 'phpArray',
            'base_dir' => __DIR__ . '/../../resources/languages',
            'pattern' => '/%s/Zend_Validate.php',
        ),
    ),
),

Hierbij zorgt regel 7 ( ‘locale’ => ‘nl_NL’) ervoor dat de default locale van de gehele applicatie op nl_NL komt te staan. Uiteraard kan dit ook via bijvoorbeeld een call in de bootstrap gedaan worden op basis van taalinstellingen browser, url of wat voor leuks dan ook.

Forceren validation error translation: module.php

Dan de verassing; de vertalingen van de validatie errrors zijn niet vertaald. Om dit te forceren moet je in de bootstrap van de module het volgende toevoegen;

public function onBootstrap($e)
{
    $translator = $e->getApplication()->getServiceManager()->get('translator');
    AbstractValidator::setDefaultTranslator($translator);
}

Met een beetje geluk zijn nu de validatie errors van de forms netjes vertaald naar de Nederlandse versie.

Skydrive synchronisatie problemen

Het kan soms voorkomen dat Microsoft Skydrive stopt met synchronisatie. Hij geeft een minimalistische error zonder een specifiek bestand of map als schuldige te benoemen. Het herstarten van de synchronisatie werkt niet en enige optie lijkt het compleet verwijderen van de gesynchroniseerde map om deze opnieuw aan te maken.

Oplosbare errors

Toch zijn er een paar errors welke nog wel oplosbaar zijn. Sowieso loopt hij eruit bij bestandsnamen die onmogelijke karakters bevatten of te lang zijn – komt voort uit de Sharepoint web-structuur die onderhevig is aan andere technische kaders dan een normaal filesysteem. Zie ook de FAQ hierover op de Microsoft site.

Daarnaast kan het soms voorkomen dat hij een error ondervindt bij het uploaden van een map of bestand wat als gevolg heeft dat de cache defect is. Er is dan geen specifieke plek die fouten geeft maar hij is ook niet meer in staat opnieuw de synchronisatie op te zetten.

Kijk in het overzicht wat de laatste succesvolle synchronisatie was en ga naar
C:\Users\[jouwgebruikersnaam]\AppData\Local\Microsoft\Office\15.0\OfficeFileCache

En verwijder hier de file/files die aangemaakt zijn rondom de tijd van de laatste succesvolle synchronisatie. Met een beetje geluk bespaar je hiermee een complete ontkoppeling en herkoppeling van de synchronisatie.

Zend Framework 2 controller based layout

In Zend Framework 2 kan het soms wenselijk zijn een afwijkende layout te configureren voor een controller. Bijvoorbeeld om een admin & front controller te dienen met een afwijkende layout. Er zijn hiervoor een aantal voorbeelden te vinden maar was zelf niet helemaal tevreden hierover dus kwam uiteindelijk met het volgende om een verschil te maken tussen de PageAdminController.php & PageFrontController.php

module.config.php


return array(
    'view_manager' => array(
        'template_map' => array(
            'layout/layout' => __DIR__ . '/../view/layout/default.phtml',
            'layout/admin' => __DIR__ . '/../view/layout/admin.phtml',
            'layout/front' => __DIR__ . '/../view/layout/front.phtml',
        ),
    ),
);

module.php


public function onBootstrap($e)
{
    $eventManager = $e->getApplication()->getEventManager();
    $eventManager->getSharedManager()->attach(
        'Zend\Mvc\Controller\AbstractActionController',
        'dispatch',
        function ($e) {
            $controller = $e->getTarget();
            $ccString = preg_split('/(?=[A-Z])/', get_class($controller));
            $layoutType = strtolower($ccString[count($ccString) - 2]);
            $config = $e->getApplication()->getServiceManager()->get('config');
            if (isset($config['view_manager']['template_map']['layout/' . $layoutType])) {
                $controller->layout('layout/' . $layoutType);
            }
        }
    );
}

Wat hij nu doet is in de module.php op basis van de controller-naam controleren of er een layout gedefinieerd is. Zo niet dan doet hij niets en gebruikt hij de default (layout/layout). Bestaat hij wel dan gebruikt hij bijvoorbeeld de layout/admin of layout/front.