Saturday, April 9, 2011

If your Phar doesn't work well ...

Do you know Phar extension? Here is a introduction in PHP official manual below:
What is phar? Phar archives are best characterized as a convenient way to group several files into a single file. As such, a phar archive provides a way to distribute a complete PHP application in a single file and run it from that file without the need to extract it to disk. Additionally, phar archives can be executed by PHP as easily as any other file, both on the commandline and from a web server. Phar is kind of like a thumb drive for PHP applications. (via. http://jp.php.net/manual/en/intro.phar.php)

Phar requires PHP 5.2.0 or newer. If your PHP is PHP 5.3, the Phar extension is build into PHP in default, otherwise Phar may be installed via the PECL extension with previous PHP versions.
If your Phar doesn't work well after you installed; the error has occured and so on, you have to check your php configure below.

check configure options

If you installed or compiled php with "--enable-zend-multibyte" option, Phar doesn't work well. So you have to recompile your php without "--enable-zend-multibyte".
For example, you installed php with brew, you have to modify your file.

Launch your text editor, and comment out the "--enable-zend-multivyte" line and save it.

    $ brew edit php

      "--enable-bcmath",
      "--enable-calendar",
      "--enable-memcache",
#      "--enable-zend-multibyte",
      "--with-openssl=/usr",
      "--with-zlib=/usr",
      "--with-bz2=/usr",

Suhosin Phar "URL not allowed"


If you use Suhosin PHP, Phar doesn't work well becase of its high security setting. So you have to check here.
In addition, Kagaya Masaki mailed me how to modify the php.ini for the environment of Suhosin PHP.
You may have to check your php.ini and modify it:

    detect_unicode = Off
    suhosin.executor.include.whitelist = phar

(via. http://www.php.net/manual/ja/phar.using.intro.php#100127)

I hope these tips will help you :)

Sample Micro Application "MameForm" which is made by Sliex

Silex is a microframework written in PHP. It's inspired by sinatora. You have only to do is to require silex.phar!

A new official web site of Silex has been published, so I tried to create a sample application with it.



At this time, I decided to create a simple contact form application with it. there are 2 screens, entry and complete one. when users complete, an email will be sent to the administration.


Phar



[Notice] If your PHP is compiled with "--enable-zend-multibyte", Phar doesn't work. Check your compile option.


Install MameForm



On the official site, you can read the detail documentation about Silex.

Now you can get my sample application code by my git repository.

    $ git clone git://github.com/brtriver/MameForm.git ./MameForm
    $ cd ./MameForm
    $ git submodule init
    $ git submodule update

This sample application use Twig for a template engine and SwiftMailer4 for sending emails, so you have to call submodule init and update.


Structure of MameForm



The structure of MameForm is very simple because of phar archive.
index.php file is a front controller file and which we write logics code in.
"views" directory is to set twig templates, in this application, only 4 templates are set: base.twig (layout), entry.twig, complete.twig and mail.twig(used for mail body).

There are some .htaccess files to deny to access this directory directly.




Call the application



simple call

Then we read index.php.
It is very simple to use Silex. You have only to write below:

  
    require __DIR__.'/silex.phar';
    use Silex\Application;
  
    $app = new Application();
    ....
    $app->run();


Entry Page



If you install MameForm in your local environment, you can see below with accessing like "http://localhost/MameForm"



Then let's see the code to be execused in index.php

  
    ...
    // entry
    $app->get('/', function() use ($app) {
        return $app['twig']->render('entry.twig', array());
    });

Silex helps us to write easily. If the request method is "GET", $app->get method is called and because of our using anonymouse function, this code is very readability.
In this code, there is no logic in it: you have only to return the results which is rendered by Twig.


Validation



Silex has no library or extention to validate, so this time, I wrote the code in index.php below: It's not best way to write validation code in index.php directly, if you always use Silex, You have to prepare your validation library with a Silex extention.


  ...
  // send email
  $app->post('/', function() use ($app) {
   // validate
   $errors = array();
   // check empty
   foreach (array('name', 'email', 'message') as $k) {
   if (trim($app['request']->get($k)) === "") {
   $errors[] = sprintf("%s is required.", $k);
   }
   }
   // check email
   if (!filter_var($app['request']->get('email'), FILTER_VALIDATE_EMAIL)) {
   $errors[] = sprintf("email is not valid.");
   }
   // send email
   if (count($errors) === 0) {
            //after sending email, redirect to complete page.
   }
   return $app['twig']->render('entry.twig', compact('errors'));
  });

If the request method is "POST" and access to "/", $app->post("/") method is called. If errors are occured, an error message pushed to the $errors array. It's simple.


Before Filter



I want to use request parameters in templates. in default, we can access to "{{app.requset.request.get('name')}}" but it's not simple, so I made a shorter name for this property like this:


  ...
  // filter
  $app->before(function() use($app){
   // assign request parameters to "request" for Twig templates with shorter name
   $app['twig']->addGlobal('request', $app['request']->request);
  });

Thanks to this fileter, I can do access to the request parameters like "{{request.get('name')}}", It's very read readability.

Finally, the template of the entry page is like below:



{% extends "base.twig" %}
{% block title %}Entry Page{% endblock %}
{% block content %}

 <h1>Contact Form</h1>

 <ul class="error">
{% for error in errors %}
   <li>{{error}}</li>
{% endfor %}
 </ul>

 <div id="entryForm">
   <form method="post" action="{{app.request.baseUrl}}/">
   <div id="formName">
       <label for="Name">Name:</label>
       <input type="text" name="name" id="Name" value="{{request.get('name')}}">
     </div>
     <div id="formEmail">
       <label for="Email">Email:</label>
       <input type="text" name="email" id="Email" value="{{request.get('email')}}">
     </div>
   <div id="formMessage">
       <label for="Message">Message:</label><br>
       <textarea name="message" rows="20" cols="20" id="Message">{{request.get('message')}}</textarea>
      </div>
      <div>
       <input type="submit" name="submit" value="Submit" class="submit-button">
      <div>
   </form>
 </div>
{% endblock %}


Thanks to Twig, the default params are escaped so we don't need to care of XSS.


Send Emails



This sample code is similar to demo code in the official documentation, only in this code, we can use Twig for body content.


  ...
// send email
if (count($errors) === 0) {
   $body = $app['twig']->render('mail.twig'); // set Twig template
   $message = \Swift_Message::newInstance()
       ->setSubject(EMAIL_SUBJECT)
       ->setFrom(array($app['request']->get('email')))
       ->setTo(array(EMAIL_ADDRESS_FROM))
       ->setBody($body);
   $transport = \Swift_MailTransport::newInstance();
   $mailer = \Swift_Mailer::newInstance($transport);
   $mailer->send($message);
   return $app->redirect($app['request']->getBaseUrl() .'/complete');
}


After sending a email, Silex force to redirect to the complete page. this redirect function is default method in Silex.


Conclusion



The total line number which I had to write is only 65. I think it is very shorter than if we write plain php codes.
It is not difficult to add a new feature to your Silex application because of the extension and it is easy to upload your Silex application to your server by FTP because of a phar archive.
If you want to watch another code written by Silex, there is Sismo,which is brought to you by Fabien Potenci. It's very useful to understand Silex.