Blog
- Details
Serve PHP7 using PHP7-fpm and Apache 2.4
By default, in Ubuntu 16.04 Xenial, installing the packages for PHP, PHP-fpm, and Apache does not result in a working php website.
The extra configuration required follows:
Install Apache2
> sudo apt-get -y install apache2
Install php, with some common packages
> sudo apt-get -y install php php-cli php-mbstring php-gd php-mcrypt php-pgsql php-mysql php-xml php-curl php-intl
Create your own apache vhost config
> vi /etc/apache2/sites-available/vhosts
<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>
Enable your vhost config
> sudo a2ensite vhosts
Disable the defualt config
> sudo a2dissite 000-default
And the missing parts of conflagration:
Enable php7-fpm
> sudo a2enconf php7.0-fpm
Enable rewrite so frameworks such as Symfony, Zend, Larvel, etc work with 'pretty' urls
enable proxy so apache can send php requests to php7-fpm
> sudo a2enmod rewrite proxy proxy_fcgi
And restart apache
> sudo systemctl restart apache2
PHP should be working now
> vi /path/to/webroot/phpinfo.php
<?php
phpinfo();
End of document. Thanks for reading.
- Details
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 apacheor
> 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.
- Details
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;
use 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.
- Details
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 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.