Monday, March 26, 2018

Authorization configuration has changed between Apache 2.2 and 2.4

If you are developing Symfony, Zend, Laravel, or any web framework requiring .htaccess,
and you upgrade Apache from 2.2 to 2.4, or your dev Vagrant vm is updated to a newer OS, your web site may start showing the ambiguous error message
“Forbidden You don't have permission to access / on this server”

There are a multitude of possible causes for this error, but assuming you web site was working, and you just upgraded Apache, check the Apache virtual host configuration as the Authorization configuration has changed between Apache 2.2 and 2.4

Ubuntu
> sudo vi /etc/apache2/sites-enabled/[your vhost site].conf

Centos
> sudo vi /etc/httpd/conf.d/[your vhost site].conf


Prior Configuration

<VirtualHost *:80>
    ServerName tutorial.localhost
    DocumentRoot /path/to/tutorial/web
    SetEnv APP_ENV "dev"
 
    <Directory /path/to/tutorial/web>
        AllowOverride All
        Order allow,deny    #<-- 2.2 config
        Allow from all        #<-- 2.2 config
    </Directory>
</VirtualHost>

Updated Configuration

<VirtualHost *:80>
    ServerName tutorial.localhost
    DocumentRoot /path/to/tutorial/web
    SetEnv APP_ENV "dev"
    <Directory /path/to/tutorial/web>
        AllowOverride All
        Require all granted   #<-- 2.4 New configuration
    </Directory>
</VirtualHost>

Ah the simple things

restart apache

Ubuntu
> sudo systemctl restart apache2

Centos
> sudo service httpd restart

References:
http://httpd.apache.org/docs/2.4/upgrading.html#run-time
https://stackoverflow.com/a/24665604/3893727


Related info:
You can also make sure apache has permission to your web root
> ls -l /path/to/tutorial/web

Ubuntu
www-data should have access

Centos
apache should have access

Or find whatever process name Apache is running as
> ps -aef | grep apache
or
> ps -aef | grep httpd

If your web root is a personal directory, you can add your user to the Apache group

Ubuntu
> sudo usermod -a -G www-data vagrant

Centos
> sudo usermod -a -G apache vagrant


End of document. Thanks for reading.

Monday, March 19, 2018

Symfony custom database exception

For Symfony2, when your database is down, the default server error 500 message is shown.
To instead show a more appropriate 503 service unavailable message, you need to create custom exception listener. While the custom exception listener will listen to all exceptions, you can specify exception type checks inside it.

In your services.yml you need to specify the listener:
> src\Acme\AppBundle\Resources\config\services.yml

services:
    kernel.listener.your_pdo_listener:
        class: Acme\AppBundle\EventListener\YourExceptionListener
        tags:
           - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

Now you need to create the listener class:
> src\Acme\AppBundle\EventListener\AcmeAppBundleListener.php

use Symfony\Component\HttpKernel\Event\GetResponseEvent,
    Symfony\Component\HttpKernel\KernelEvents;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;

use PropelException;
// use DoctrineException;

class YourExceptionListener implements EventSubscriberInterface
{
        $env = $this->container->getParameter('kernel.environment');
        if ($env == 'dev' || $env == 'test') {
            // show default exception
            return;
        }

        $exception = $event->getException();

        // only customize propel or doctrine ie database exceptions
        // if (!$exception instanceof DoctrineException) {
        if (!$exception instanceof PropelException) {
            // show default exception
            return;
        }     

        $path = $event->getRequest()->getPathInfo();

        $response = new Response();

        // set status text
        $exception_msg = $exception->getMessage();
        if (stripos($exception_msg, 'Unable to open PDO connection') !== false) {
            $response->setStatusCode(Response::HTTP_SERVICE_UNAVAILABLE);
            $status_text = $this->container->getParameter("message.error.unable_to_connect");
        } else {
            // show default exception
            return;
        }

        // add exception code, if set
        $exception_code = $exception->getCode();
        if ($exception_code > 0) {
            $status_text .= " [".$exception_code."]";
        }

        $response->setContent(
            $this->templating->render(
                'AcmeAppBundle:Exception:exception.html.twig',
                [
                    'status_code' => $response->getStatusCode(),
                    'status_text' => $status_text
                ]
            )
        );

        $event->setResponse($response);
}

Add the message configuration:
> src\Acme\AppBundle\Resources\config\messages.yml

parameters:
    message.error.unable_to_connect:        "Unable to connect to service at the moment"


Create the exception template:
> src\Acme\AppBundle\Resources\views\Exception\exception.html.twig

<!DOCTYPE html>
<html>
<head>
    <meta charset="{{ _charset }}" />
    <title>An Error Occurred: {{ status_text }}</title>
</head>
<body>
<h1>Zork! An Error Occurred</h1>
<h2>The server returned a "{{ status_code }} {{ status_text }}".</h2>

<div>
    This should be temporary, but if not, please let us know what you were doing when this error occurred.
    We will fix it as soon as possible. Sorry for any inconvenience caused.
</div>
</body>
</html>

Stop your database or in your code throw DoctrineException or PropelException and you should see the new exception page.

Reference
https://stackoverflow.com/questions/29732630/catching-database-exceptions-in-symfony2

End of document. Thanks for reading.

Monday, March 12, 2018

PHPUnit exceeds memory limit

If you happen to be stuck running PHPUnit 4.8 or such,
you many run into memory limits when you run a large number of tests.

With PHPUnit 4.8, you can add some extra code to clear out the variables you created during each test, thus saving some memory.

class YourTestCase extends WebTestCase
{
   protected function tearDown()
  {
      echo 'pre reduce memory usage: '.sprintf('%.2fM', memory_get_usage(true)/1024/1024);

      // reduce memory usage
 
      // get all properties of self
      $refl = new \ReflectionObject($this);
      foreach ($refl->getProperties() as $prop) {
          // if not phpunit related or static
          if (!$prop->isStatic() && 0 !== strpos($prop->getDeclaringClass()->getName(), 'PHPUnit_')) {
              // make accessible and set value to free memory
              $prop->setAccessible(true);
              $prop->setValue($this, null);
          }
      }
      echo 'post reduce memory usage: '.sprintf('%.2fM', memory_get_usage(true)/1024/1024);

      parent::tearDown();
  }
}

Reference
https://stackoverflow.com/questions/13537545/clear-memory-being-used-by-php    

Note: Using unset($this) or unset($var) does not immediately free the variable from memory.  However, setting $this = null or $var = null immediately assigns the value null, which uses less memory than the object, string, or other value that was set.  You could also use the value of 0 or "" or anything really, just null is short and sweet and infers the intent of the value being unset.

Related extra
You can also increase the allowed memory usage in your test.

class YourTestCase extends WebTestCase
{
    public function setUp()
    {
        ini_set('memory_limit', '512M');

        parent::setUp();
    }
}

End of document. Thanks for reading.

Monday, March 5, 2018

fix composer "The lock file is not up to date with the latest changes in composer.json"


to only update the composer lock file

  • try 1

> composer update 
this will update all packages and their dependencies, plus updating the lock file

note:
you can pin you main dependencies with exact versions
ie 2.8 instead of ~2 or ^2 or @dev (eek!)

  • try 2

> composer --lock update  
--lock: only updates the lock file hash
but actually, this will update dependencies too, which are not version pinned

  • try 3

> composer --root-reqs --lock update  
    --lock: Only updates the lock file hash
    --root-reqs: Restricts the update to your first degree dependencies
   
and since your first degree dependencies are version locked, right?
only the lock file gets updated.
task accomplished.

End of document. Thanks for reading.