Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

Friday, October 16, 2015

Custom 404 Page in Slim Framework 3

In the past month, I've been learning Slim 3 PHP framework through a website as a real experiment of how powerful Slim Framework is. In this post, I'm sharing a code snippet for rendering 404 page.

Between creating a Slim Container and creating the Slim App, I override the default notFoundHandler.
$container = new Slim\Container;

...

// Override the default Slim Not Found Handler
$container['notFoundHandler'] = function ($c) {
    return function ($request, $response) use ($c) {
        return $c['view']->render($response, '404.html', [])->withStatus(404);
    };
};

...

// Initialize the app
$app = new Slim\App($container);

In my case, I use Twig template engine for views. If your case is different, change the render line.

File System Caching in Slim Framework 3

In the past month, I've been learning Slim 3 PHP framework through a website as a real experiment of how powerful Slim Framework is. In this post, I'm sharing a code snippet of how I implemented the Filesystem caching.

The summary is:
- Add a middleware for caching successful responses (200 OK) by saving the response in a file. To avoid long file names or deep paths, I hash the response URL.
- When receiving a request, check its hashed path in the cached files. If found, return the content. Else, continue with initializing the app, routes, and everything else. This way, there is no initialization overhead for cached requests.

Here is the beginning of my index.php:
require './lib/initializer.php';

function getCachePath($uri) {
    $webpage_handle = md5($uri);
    return ROOT."/public/cache/".$webpage_handle;
}

/* Check for cached file here before loading anything or opening DB */
if(CACHING_ENABLED) {
    $requestedURI = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
    $cachedFilePath = getCachePath($requestedURI);
    if (file_exists($cachedFilePath) && (filemtime($cachedFilePath) > (time() - CACHE_SECONDS ))) {
        $cacheFile = file_get_contents($cachedFilePath);
        die($cacheFile);
    }
}

$cacheMiddleware = function ($request, $response, $next) {
    /* process the request */
    $response = $next($request, $response);

    /* cache response */
    if(CACHING_ENABLED && ($response->getStatusCode()==200) && ($request->getMethod() == 'GET')) {
        $cachedFilePath = getCachePath($request->getUri());
        $file = fopen($cachedFilePath,"w");
        fwrite($file, $response->getBody());
        fclose($file);
    }

    return $response;
};


First, I require a file where I initialize some configs and constants, like CACHE_SECONDS.

Then, I create a function for deciding the cache path for requests. To use it in both reads and writes.

Then, -if caching is enabled- I check for the existence of the requested file and the creation date. This way I override the file if it was older. (*check the end of the post for another better way for expiry)

Next, you can see the caching middleware I use with some routes I wish to cache. Here is an example:
$app->get('/', function ($request, $response, $args) {
    // some app-related code
})->setName('home')->add($cacheMiddleware);

And that's it. The file system is now implemented with minimal code. Of course there are many changes and upgrades depending on your case.

----------------------------------
Notes:
*Better than just checking for file creation date (and leaving the files to increase), you can remove the date-check part and add a cron job for removing files older than a specific duration. Here is how my cron job script looks like:
CACHE_DIR=$1;
cd $CACHE_DIR && find * -type f -cmin +5 -exec rm {} \;

It takes the path to clean in the cron job command. And removes any file older than 5 minutes.

The cron job looks like:
*/5 * * * * bash ~/path/to/cache/expiry/script.sh ~/path/to/cache/folder/ 



Saturday, August 29, 2015

Arabic-friendly Exeption reporting in Slim Framework 2 & 3

Today I've been upgrading my Slim PHP project from version 2 to version to version 3 beta. And one of the issues I had was reporting my Arabic exceptions from some models I have. I did this before in version 2, to end up with moving from this scrambled message:


to this one:

In Slim Framework 2, I had to edit Middleware / PrettyExceptions.php to add a meta tag for content UTF-8 encoding:
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>

Now, when I moved to Slim Framework 3.0.0-beta2, I had to do this again but in Handlers/Error.php.

I hope that this trick helps someone on the planet. And I think I may create a pull request for this small hack.

Update: pull request has been merged. (#1470)

Friday, June 5, 2015

Nginx + Slim Framework + phpMyAdmin on Ubuntu

Yesterday I was giving Slim Framework a test ride on my Ubuntu machine. But the machine was not ready with Nginx, or phpMyAdmin (I needed it to check the output of an ORM called RedBean). So I'm writing what I did exactly for my reference and for other's benefit.




First, in case you don't have any of the following packages, go ahead and do it

Installations:


Install MySQL
sudo apt-get install mysql-server php5-mysql

and remember your root password you will enter in the installation process.

Install Nginx
sudo apt-get install nginx


Install PHP-FPM (PHP FastCGI Process Manager)
sudo apt-get install php5-fpm


Install phpMyAdmin
sudo apt-get install phpmyadmin

During installation, you will be asked to choose your web server (apache2/lighttpd). But since we will use Nginx, just press the TAB key then Enter. Then you will be asked if you want to proceed with phpMyAdmin database configurations, choose yes and continue then enter your MySQL root password when asked to.



Configurations:

FPM

sudo nano /etc/php5/fpm/php.ini

or as I prefer
sudo gedit /etc/php5/fpm/php.ini

Find the line, cgi.fix_pathinfo=1. Uncomment it and change the 1 to 0.

Then
sudo gedit /etc/php5/fpm/pool.d/www.conf

and find the value given to listen =, to be used later in Nginx configuration.
It should be /var/run/php5-fpm.sock or 127.0.0.1:9000. 

Now restart fpm
sudo service php5-fpm restart

Nginx

Now let's configure Nginx for both phpMyAdmin and Slim Framework project.

sudo gedit /etc/nginx/sites-enabled/default


And add the configurations for both phpMyAdmin and the Slim Framework project.

server {
    listen 80;
    server_name phpmyadmin.local;
    root /usr/share/phpmyadmin;
    index index.php;
    location / {
        try_files $uri $uri/ /index.html;
    }
    # pass the PHP scripts to FastCGI server listening on the php-fpm socket
    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;       
    }
}

server {
    listen 80;
    server_name slimtest.local;
    root /home/madly/slim;
    try_files $uri /index.php;
    # pass the PHP scripts to FastCGI server listening on the php-fpm socket
    location /index.php {
        fastcgi_connect_timeout 3s;     # default of 60s is just too long
        fastcgi_read_timeout 10s;       # default of 60s is just too long
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;      
    }
}

Now let's restart Nginx server to apply the changes
sudo service nginx restart


Notes on these server configs:

1- The fastcgi_pass value is set to unix:/var/run/php5-fpm.sock because of the value I mentioned earlier in fpm configs. It may be 127.0.0.1:9000.

2- I chose to listen to port 80 but with different server names. So I must edit my hosts file
sudo gedit /etc/hosts
and add these two lines
127.0.0.1 phpmyadmin.local
127.0.0.1 slimtest.local 

3- During my tests with these configs, I had the css/img files accessible in my project with paths like
<link rel="stylesheet" type="text/css" href="/css/main.css">
by having a project structure like this


A problem I had:
The following day I had a problem with a project giving me the page "no input file specified". After inspecting the "/var/log/nginx/error.log" file, I found "(13: Permission denied)" errors. So I had to add extra permissions to the project:
chmod -R 755 ./