Sep 19

Moving to GitHub

For my projects (actually the two Symfony 2 bundles I develop, ElendevImageBundle and ElendevCheckoutBundle), I used Google Code under Git as source management.

Lots of projects I use (Symfony 2 and some Symfony 2 bundles) use GitHub. Version 2.1 of Symfony now use Composer as dependency manager and Composer use mainly http://packagist.org/ as repository.

I found that packagist can use GitHub to provide packages and the final goal is to provide my bundles through Composer to make them easier to include into applications.

 New access

You can now access the ElendevCheckoutBundle at https://github.com/Elendev/ElendevCheckoutBundle.

And the ElendevImageBundle at https://github.com/Elendev/ElendevImageBundle.

The both pages (ElendevCheckoutBundle and ElendevImageBundle)  are updated with new configuration.

Jan 16

Plevin : routing mechanism

Introduction

In this article I’ll present the routing mechanism inside my home-made php framework : Plevin.

The routing mechanism inside Plevin is quite simple. Every controller (named here handler) is market with an annotation and a file is generated at website initialisation.

Controller (handler) class

Here you can see a simple example of controller. The CategoryHandler class is marked as handler and manage specified urls.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * @HandleURL(pattern="/categories")
 */
class CategoryHandler{
    /**
     * @Injection(id="Logger")
     * @var Plevin\logging\Logger
     */
    private $logger;
 
    /**
     * @HandleURL(pattern="/$")
     */
    public function showMain(){
        echo "Show main page";
    }
 
    /**
     * @HandleURL(pattern="/{category}")
     */
    public function showCategory($categoryName = "main") {
        echo "Template content for category $categoryName";
    }
 
    /**
     * Show the specified object, from the specified category
     * @HandleURL(pattern="/{category}/{object}$")
     */
    public function showObject($categoryName, $objectName) {
        echo "Template content for category $categoryName, object $objectName";
    }
}

The handler class has a function for every managed url. Each function can receive url parameters and have to generate a template.

In the example above a simple injection is made, the handler receive a reference to the framework’s logger.

Generated file

On first execution, the CategoryHandler is analysed and the corresponding generated cached file is visible here :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if(preg_match("@^/categories@", $query)){
    if(!file_exists("/path/to/app/Site/handler/CategoryHandler.php") || filemtime(__FILE__) < filemtime("/path/to/app/Site/handler/CategoryHandler.php"))
        throw new \Plevin\cache\CacheOutOfDateException("Class Shop2\handler\CategoryHandler out of date !");
    if(preg_match("@^/categories/$@", $query, $params)){
        $injection = new \Plevin\injection\InjectionProxy("\handler\CategoryHandler");
        array_shift($params);
        $injection->__call("showMain", $params);
        return true;
    }
    if(preg_match("@^/categories/((?:[a-zA-Z0-9]|_|-)+)$@", $query, $params)){
        $injection = new \Plevin\injection\InjectionProxy("\handler\CategoryHandler");
        array_shift($params);
        $injection->__call("showCategory", $params);
        return true;
    }
    if(preg_match("@^/categories/((?:[a-zA-Z0-9]|_|-)+)/((?:[a-zA-Z0-9]|_|-)+)$@", $query, $params)){
        $injection = new \Plevin\injection\InjectionProxy("\handler\CategoryHandler");
        array_shift($params);
        $injection->__call("showObject", $params);
        return true;
    }
}

First of all, the generated file check if the handler has been modified since last file generation. When the generated file is older than the handler, an CacheOutOfDateException is thrown and the framework generate a new cache file.

The controller routing is managed with some simple preg_match function. Before any call to the handler’s method, a new instance of InjectionProxy is created and the correct handler’s method class is made through the newly generated InjectionProxy. Every dependency injection is managed by the InjectionProxy (in this case, the logger is injected if necessary). For more informations about dependency injection, an article will be available soon.

The generated file return true when an url is recognized and managed by a handler. When no handler manage a given url, false is returned and a simpl 404 error is generated.

Link creation

Plevin framework provide a great flexibility in link management. A link to a specific page is a link to a specific controller’s method with specific parameters. An URLManager is provided to help developer creating links. The code below show how to create a link to a specific controller’s method :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * @HandleURL(pattern="/inscription")
 */
class InscriptionHandler {
 
    /**
     * @Injection(id="URLManager")
     */
    private $urlManager;
 
    /**
     * @HandleURL(pattern="/$")
     */
    public function showInscriptionForm() {
        ...
        $link = $this->urlManager->getURL("App\handler\CategoryHandler->showCategory", array("category" => $currentCategory));
        ...
    }
}

In the code above, the $link variable contains the path to the showCategory() method of the CategoryHandler controller. When urls contain parameters, they can be given through getURL() second argument.

The generated url is based on the @HandleURL annotation of the targeted method.

Conclusion

The url management provided by Plevin is flexible enough to change easly controllers’ paths. The main disadvantage of this operating way is the high dependency to the destination handler’s name (on refactoring you have to recreate every links).

A better method is the one used by Symfony 2 : an url file containing an id for every link. Every id target a controller’s method and links are made with a reference to a specific id.

Jan 13

Checkout management bundle for Symfony 2

Introduction

I actually work, on my spare-time, on an e-commerce website. As I had to implement Paypal ExpressCheckout payment method and because I didn’t found any CheckoutBundle matching to my needs, I decided to create one and to provide it with Apache 2 licence. Thus was born ElendevCheckoutBundle.

This bundle is still under development and only Paypal ExpressCheckout is provided. The ElendevCheckoutBundle bundle can support lots of checkout services and I plan to implement Google Checkout API and eventually Swiss post payment API.

The ElendevCheckoutBundle is available on google code and more informations are available at ElendevCheckoutBundle page.

Conclusion

The ElendevCheckoutBundle is still under heavy development but it’s already used on some of my projects. I plan to extend the bundle and add as much checkout services as possible.

Jan 11

Image management bundle for Symfony 2 / Twig

Introduction

During works on some websites, I often encountered the necessity to create thumbnails of existing images. Its allways the same problem : I need to save thumbnails on some specific directories, manage thumbnails creations on file upload and so on. So I decided to create the ElendevImageBundle for Symfony 2.

The template designer specify the image properties (size, rotation, grey scale or colored, …) and a corresponding image file is generated and saved in a cache directory. Every further call of same image with same properties returns the previously cached file.

It’s designed to be very efficient. You can find more informations on the dedicated page of ElendevImageBundle.

Conclusion

The ElendevImageBundle is still under development but the actual ElendevImageBundle is already used on some of my projects. It’s an easy way to manage image (particulary image resizing) regardless of new image files storage or specific image size management.

Dec 21

A home made PHP framework : Plevin

Introduction

Plevin means Plug Evolution In. It’s a little bit pretentious but when I started working on it I had a lot of ambitions. I’ve done a lot of great work around my home made MVC framework in PHP but I had a severe lack of time.

The framework have never been finished but there is a lot of good ideas I haven’t founded in other PHP frameworks. That’s why I will make articles presenting some of the main concepts of Plevin framework, focusing on some difficulties I’ve encountered and solutions I’ve founded.

I’ve initially based my work on Spring and JEE frameworks, using annotations in PHP class. At the time I’ve began working on this framework, PHP 5.3 just came out and provided very useful features like namespace support.

Annotation based configuration

Java, unlike PHP, support annotations in the code. Nevertheless, a simple way to use annotations in PHP is to use commented annotations. Annotations are added through comments (PHPComments, working like javadoc comments) and a simple parser is used to parse the class code and analyse annotations.

Every class (like controllers and available services) are marked with commented annotations. On first execution every file is scanned and annotations are analysed and saved into cached files. A simple example of commented annotations is visible here :

1
2
3
4
5
6
/**
 * @HandleURL(pattern="/categories")
 */
class CategoryHandler{
    ...
}

Cache system

Because of the lack of performance of PHP, the Plevin framework is based on a cache system. Every configuration (like class annotations, configuration file) is analysed and a cached version is created (most of the time it’s a simple PHP file with information in an array). A template system is available with fully cached translation system. Every page is created and cached in every language. When translation files are modified, the modified time is compared to the cached file creation time. When the cached file is older than the modified file, a new cached file is created.

The routing system is also based on cache. Every controller has an annotation containing the handled path. On first script execution, a cache file is created containing all available path routing to correct controller.

Dependency injection

An other important feature is the dependency injection system. This system is an annotation based dependency injection system. Every controllers and beans can add PHP annotations on member to have dependency injection (like @Autowire in Spring).

1
2
3
4
5
6
7
8
class CategoryHandler{
    /**
     * @Injection(id="Logger")
     */
    private $logger;
 
    ...
}

A configuration file contain services declaration :

1
2
3
4
5
6
7
8
9
10
<lookupClasses>
	<instance id="EntityManager" class="Plevin\db\ConfigAutoloadEntityManager"/>
	<instance id="TemplateFactory" class="Plevin\template\CacheLangTemplateFactory"/>
	<instance id="LanguageProvider" class="Plevin\language\Language"/>
	<instance id="PathManager" class="Plevin\DefaultPathManager"/>
	<instance id="URLManager" class="Plevin\url\DefaultURLManager"/>
	<instance id="SessionService" class="Plevin\session\SessionService"/>
	<instance id="Logger" class="Plevin\logging\Logger"/>
	<instance id="Translator" class="Plevin\language\DefaultTranslator"/>
</lookupClasses>

Many options exists for services, like statefull or stateless status. An article will be dedicated to the dependency injection mechanism.

Template system

A simple home made template engine is used to optimize translation system. A template file is compiled and cached for every available language. The template engine need to translate file only once per language. Some macros are also available, like __INSERT(…)__. The code below is a simple template example :

1
2
3
4
5
6
7
8
9
10
11
__INSERT("/main/template_header.html")__
<table width="100%">
__INSERT("/category/object_item.html")__
<tr>
    <td colspan="2">__TECHNICAL_INFORMATION__ :</td>
</tr>
<tr>
    <td colspan="2"><?php echo $object->getMainDescription();?></td>
</tr>
</table>
__INSERT("/main/template_footer.html")__

ORM

When I began the Plevin framework there was no class oriented ORM working with getters and setters, like JPA. I decided to create my own ORM layer, at first based on XML files for model description and, later, on class annotations. Class declaration and simple relations (one-to-one, one-to-many, many-to-one, bidirectionnal one-to-many) works well, database is correctly generated.

The difficulties arrived when I decided to implement bidirectional many-to-many relations with eager / lazy initialisation system and heritage.

At the same time, Doctrine 2 passed on beta version and is exactly what I was expecting from an ORM. I replaced the home made ORM with Doctrine 2.

Conclusion

Finally, Symfony 2 came out with lots of features I expected. Idecided to use it, mainly because of the Symfony 2 community support (managing a complete framework is a time-consuming task).

The work I’ve done on my home made framework wasn’t useless because I’ve learned a lots of things, that’s why I’ve decided to share my experience on my blog. I will write some articles to present some specific parts of Plevin.

Older posts «

» Newer posts