After doing some searching around the PhalconPHP documentation, forums, and Google it became apparent that one of two things were happening here: either no one that is using PhalconPHP has had to do rerouting or there is a way and it just hasn’t been shared with the rest.

Either way, after digging through the PhalconPHP API we found something that could help us with what we wanted. Therefore we created a solution to what we needed. It might not be the prettiest or best one out there, but it worked and took care of it.

The issue:

While performing SEO optimization to a site that had already been online for some time, and that had already been indexed by Google it came to our attention of URLs that were 4 and 5 levels deep. According to SEO, URLs should try to stick to being 3 levels deep, 4 at most if it cannot be avoided.

Examples of URL depths:

http://domain.com/level2/level4/level5/level6/…

Correct way:

http://domain.com/level2/level3-level4-level5-level6-…

Our solution:

Take this original route definition (ignore how unusual it maybe, it is for example’s sake):

$router->add('/level2/level3/([0-9]+)/([0-9]+)', [
    'namespace' => 'Project\\Controllers\\',
    'controller' => 'index',
    'action' => 'getLevel',
    'param1' => 'level3',
    'param2' => 1,
    'param3' => 2,
]);

We wanted to perform a 301 redirect on that URL in order to allow the new URL format without losing what had already been indexed.

We know $router is an instance of Phalcon\Mvc\Router, and when we add a route using $route->add() we get back an instance of Phalcon\Mvc\Router\Route. With that in mind we went to explore the API and found something within the Route class: beforeMatch().

A thought went across our heads, so we decided to test it.

$router->add('/level2/level3/([0-9]+)/([0-9]+)', [
    'namespace' => 'Project\\Controllers\\',
    'controller' => 'index',
    'action' => 'getLevel',
    'param1' => 'level3',
    'param2' => 1,
    'param3' => 2,
])->beforeMatch(function(){
    die(Testing beforeMatch');
})

Don’t judge. We know you use die() too. And it effectively printed the string when the URL was invoked. Neat! Well, the API doesn’t not specify any parameters, but surely there must be some, right? After all it is a callback, which means something should be sent back to us. Well, by specifying parameters we confirmed that 3 pieces of information are being sent back to the callback function:

Param 1: The match URL string. Param 2: A Phalcon\Mvc\Router\Route object. Param 3: A Phalcon\Mvc\Router object.

In the end we only needed the first two parameters in order to do what was needed.

$router->add('/level2/level3/([0-9]+)/([0-9]+)', [
    'namespace' => 'Project\\Controllers\\',
    'controller' => 'index',
    'action' => 'getLevel',
    'param1' => 'level3',
    'param2' => 1,
    'param3' => 2,
])->beforeMatch(function($matchedRoute, $routeObject){
    // do something
});

Furthermore, it came to our attention that we did not need to specify the route’s paths:

$router
    ->add('/level2/level3/([0-9]+)/([0-9]+)')
    ->beforeMatch(
        function ($matchedRoute, $routeObject) {
            // do something
        }
    )
;

Now all we had to do was work with this to get the redirect going. Through Phalcon\Mvc\Router\Route we found getCompiledPattern(). This will return the complete regular expression for the defined pattern.

$router
    ->add('/level2/level3/([0-9]+)/([0-9]+)')
    ->beforeMatch(
        function ($matchedRoute, $routeObject) {
            preg_match(
                $routeObject->getCompiledPattern(),
                $matchedRoute, 
                $params
            );
        }
    )
;

Good so far. Next we defined the URL to where we wanted to do the redirection:

$router
    ->add('/level2/level3/([0-9]+)/([0-9]+)')
    ->beforeMatch(
        function ($matchedRoute, $routeObject) {
            preg_match(
                $routeObject->getCompiledPattern(),
                $matchedRoute,
                $params
            );
            $url = '/level2/level3-' . $params[1] . '-' . $params[2];
        }
    )
;

The last thing we needed was to do the redirection. None of the parameters supplied by PhalconPHP could help us on that, so we decided to invoke our good old friend, the Dependency Injector (DI):

$router
    ->add('/level2/level3/([0-9]+)/([0-9]+)')
    ->beforeMatch(
        function ($matchedRoute, $routeObject) {
            preg_match(
                $routeObject->getCompiledPattern(),
                $matchedRoute,
                $params
            );
            $url = '/level2/level3-' . $params[1] . '-' . $params[2];
            return \Phalcon\DI::getDefault()
                ->getResponse()
                ->redirect($url, true, 301);
        }
    )
;

And there you have it. A 301 redirect in PhalconPHP directly from the routes. Call it rerouting if you want.

We are excited to announce the release of Phalcon 2.0.7! This is our seventh release in the 2.0.x series and it contains bug fixes and new features for your enjoyment!

PHP is a single inheritance language. This limitation created challenging situations for a lot of PHP developers. Thankfully this limitation was solved in PHP 5.4 with the introduction of traits.

Here is a brief overview on traits.

Traits are a very handy way of reusing code in PHP. This allowed to overcome the single inheritance limitation that PHP had. It enabled developer to reuse methods across several independent classes residing in different class arrangements. Traits reduce complexity, and allows to helps prevent problems that were associated to multiple inheritance and mixins.

Traits are very similar to classes, but are intended to group up functionality in a smooth and uniform way, and they cannot be instantiated on their own. It is an expansion to traditional inheritance and enables a horizontal scaling behavior; as in, utilization of class members without having to require inheritance.

Now, onto what you came here for.

I like to think that I am organized. I’ve been told so, but I think that there is always room for improvement. Therefore when adding traits to a project I like to create a traits directory under my app directory. Placement is not really that important, but I like to keep it clean (for example you could create the traits folder under your library folder). Here is how to looks:

The next step after creating the folder is integrating it into our working environment. Open the /app/config/config.php file and add a reference to the directory under application.

  return new \Phalcon\Config([
        'siteName'       => getenv('SITE_NAME'),
        'siteUrl'        => 'http://'.getenv('SITE_NAME'),
        'controllersDir' => getenv('BASE_DIR') . 'app/controllers/',
        'modelsDir'      => getenv('BASE_DIR') . 'app/models/',
        'viewsDir'       => getenv('BASE_DIR') . 'app/views/',
        'pluginsDir'     => getenv('BASE_DIR') . 'app/plugins/',
        'libraryDir'     => getenv('BASE_DIR') . 'app/library/',
        'cacheDir'       => getenv('BASE_DIR') . 'app/cache/',
        'traitsDir'      => getenv('BASE_DIR') . 'app/traits/'
        'baseDir'        => getenv('BASE_DIR'),
        'baseUri'        => '/'
    ]
]);

Next, open the /app/config/loader.php file and add register the namespace reference.

$loader->registerNamespaces(array(
    'Project\Controllers' => $config->application->controllersDir,
    'Project\Traits' => $config->application->traitsDir
));

$loader->register();

That’s it! Well, aside from actually creating a trait and applying for use. Take this trait for example:

namespace Project\Traits;

trait HttpBehavior
{
    /**
     * Set JSON response for AJAX, API request
     * 
     * @param  string  $content       
     * @param  integer $statusCode    
     * @param  string  $statusMessage
     * 
     * @return \Phalcon\Http\Response                 
     */
    public function jsonResponse($content, $statusCode = 200, $statusMessage = 'OK')
    {
        // Encode content
        $content = json_encode($content);

        // Create a response since it's an ajax
        $response = new \Phalcon\Http\Response();
        $response->setStatusCode($statusCode, $statusMessage);
        $response->setContentType('application/json', 'UTF-8');
        $response->setContent($content);

        return $response;
    }
}

Now, to use it just go where you want to include it and invoke it:

namespace Project\Controllers;

class IndexController extends ControllerBase
{
    /**
     * Invoke Traits
     */
    use \Project\Traits\HttpBehavior;

    /**
     * Does something...
     *
     * @param $someVar
     *
     * @return \Phalcon\Http\Response
     */
    public function doSomethingAction($someVar = '')
    {
        // Do this and that and get something to return
        $content = [
            'id' => 1,
            'message' => 'something'
        ];

        // Return response
        return $this->jsonResponse($content);
    }
}

Very simple and elegant.

The use of environment variables is quickly becoming a staple for many PHP developers. It is a way through which configurations can be easily added to the development of applications with very little work or hassle.

This is a small article to show you how to implement environment variables in your PhalconPHP configuration.

Some notes before you continue reading: I currently work using PHP 5.6 in my development environment. Therefore expect not to see array() when an array is being used or define. Expect to see the new brackets method ([]).

We will be using Vance Lucas’ phpdotenv library (the link will take you to his GitHub repository). You can follow the instructions for installing the library using Composer.

Once you have the library installed you will want to create a .env file, which is where you will define all your configuration values. It is a good idea to create this file in the user root directory (for example: /home/user/).

//This is an example of a `.env` file:
DOMAIN=yourdomain.com
BASE_DIR=/home/user/
PRODUCTION=1

DATABASE_HOST=
DATABASE_USER=
DATABASE_PASS=
DATABASE_NAME=

BEANSTALK_HOST=
BEANSTALK_PORT=
BEANSTALK_PREFIX=

MEMCACHE_HOST=
MEMCACHE_PORT=

REDIS_HOST=
REDIS_PORT=

EMAIL_HOST=
EMAIL_PORT=
EMAIL_USER=
EMAIL_PASS=

With this you can set all necessary configuration values. Do keep in mind that different development systems can (and probably will) have different values for the configurations. Your .env file is to be kept off your versioning system. So, in order to let the other developers know what values they NEED to set for the configuration all you need to do is create a .env.example file which is an exact copy of .env, except that none of the variables will have values.

This skeleton file will let the developer know what he has to provide for the system in order for it to work properly.

Next, we will load the environment variables into PhalconPHP. Locate the loader.php file (located in INSTALLATION_DIR/app/config/loader.php), open it in your editor of preference and append these lines to the end of the file:

/**
 * Composer autoload
 */
include __DIR__ . '/../../vendor/autoload.php';

/**
 * Environment variables
 */
$dotenv = new Dotenv\Dotenv(__DIR__ . '/../../');
$dotenv->load();

IMPORTANT This environment variables loader has to go AFTER you include Composer’s autoload.php file. Look at the code above carefully.

With this we are ready to load our environment variables into PhalconPHP’s configuration. So go ahead and open the config.php file (in the same folder as the loader.php file), and use the environment variables like this:

 [
        'adapter'  => 'Mysql',
        'host'     => getenv('DATABASE_HOST'),
        'username' => getenv('DATABASE_USER'),
        'password' => getenv('DATABASE_PASS'),
        'dbname'   => getenv('DATABASE_NAME')
    ],
    'application' => [
        'controllersDir' => getenv('BASE_DIR') . 'app/controllers/',
        'modelsDir'      => getenv('BASE_DIR') . 'app/models/',
        'viewsDir'       => getenv('BASE_DIR') . 'app/views/',
        'pluginsDir'     => getenv('BASE_DIR') . 'app/plugins/',
        'libraryDir'     => getenv('BASE_DIR') . 'app/library/',
        'cacheDir'       => getenv('BASE_DIR') . 'app/cache/',
        'baseUri'        => '/',
        'baseDir'        => getenv('BASE_DIR')
    ]
]

That’s it! Couldn’t have been easier! With this in place you can now commit ALL project files without having to worry for the configurations the different systems need.

We are excited to announce the release of Phalcon 2.0.6!

This version contains mostly bug fixes as well as improvements to the 2.0.x series.

We are happy to announce that Phalcon 2.0.5 is released.

This version contains bug fixes and small improvements to the 2.0.x series.

As part of our three to five weeks minor release schedule, we are excited to announce that Phalcon 2.0.4 has been released!

The number of improvements and bug fixes are a lot more compared to other releases in the 2.0.x series:

Phalcon Process

The following post explain our release process:

As part of our regular release schedule, we are happy to announce that Phalcon 2.0.3 has been released!

This version contains many bug fixes as well as new functionality that derived from community NFRs.

Projects
We're a nonprofit organization that creates solutions for web developers. Our products are Phalcon, Zephir and others. If you would like to help us stay free and open, please consider supporting us.