Phalcon 3.0.0 released

Read time: 21 minutes

The Phalcon team is very excited to share some news with our community!

The last few months, we have been working hard to push 2.1 out, which contains significant enhancements as well as some API changes that require attention so as not to break compatibility with your application. On top of that we have been working in making Zephir PHP7 compatible so that you can enjoy Phalcon in your PHP7 application. Some news first though:

Versioning

For any future Phalcon releases we are adopting SemVer (https://semver.org). In short:

Given a version number MAJOR.MINOR.PATCH, increment the:

  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards-compatible manner, and
  • PATCH version when you make backwards-compatible bug fixes.
  • Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

Since 2.1 has many API changes, we decided that it would be best to not release it as is and start using SemVer to better communicate with the community and keep track of our releases.

2.1 is dead, all hail 3.0

As mentioned above, 2.1 will not be fully backwards compatible. As a result, we are changing the version number to 3.0.

PHP version support

The Phalcon team takes security very seriously and thus have decided to provide support to PHP versions that are supported. As of 3.0, PHP 5.3 and 5.4 will be deprecated. We are making a small exception to this rule and will continue to support 5.5 for a little while, but since its support has expired a few days ago, it will too be deprecated in a future release.

The goodie bag

So what does 3.0 offer? The changelog is extensive as you can see. Below are highlights of the changes as well as areas you need to concentrate.

• PHP 5.3 and 5.4 are fully deprecated. You can compile the code on your own, but we will not be able to support it nor can we guarantee that it will work as you expect it to. PHP 5.3 support expired mid 2014 and 5.4 expired mid 2015. We need to ensure our applications have all known vulnerabilities on the PHP side fixed and patched, thus we will not support any unsupported PHP version. This excludes PHP 5.5, whose support expired a few days ago. We will deprecate 5.5 in a future release but will make sure that you all know beforehand so that you can prepare.

INCOMPATIBLE: You will need to upgrade your PHP installation to 5.6. You can always continue to use the Phalcon version you are using, but in 3.0 support for PHP 5.4 has been deprecated and we cannot guarantee that PHP 5.5 will be fully functional.

APPLICATION

Phalcon\Cli\Console and Phalcon\Mvc\Application now inherits Phalcon\Application. This change makes the interfaces more uniformed and offers additional functionality to the respective applications (cli/mvc)

BEANSTALK

• Added \Phalcon\Queue\Beanstalk::ignore(). Removes the named tube from the watch list for the current connection.

• Added \Phalcon\Queue\Beanstalk::pauseTube(). Can delay any new job being reserved for a given time.

• Added \Phalcon\Queue\Beanstalk::kick(). It moves jobs into the ready queue. If there are any buried jobs, it will only kick buried jobs. Otherwise it will kick delayed jobs.

// Kick the job, it should move to the ready queue again
if (false !== $job->kick()) {
    $job = $this->client->peekReady();
}

• Added \Phalcon\Queue\Beanstalk::listTubeUsed(). Returns the tube currently being used by the client.

• Added \Phalcon\Queue\Beanstalk::listTubesWatched(). Returns a list tubes currently being watched by the client.

• Added \Phalcon\Queue\Beanstalk::peekDelayed(). Return the delayed job with the shortest delay left.

$this->client->put('testPutInTube', ['delay' => 2]);
$job = $this->client->peekDelayed();

• Added \Phalcon\Queue\Beanstalk::jobPeek(). Returns the next available job.

$this->client->choose(self::TUBE_NAME_1);
$jobId = $this->client->put('testPutInTube');
$job   = $this->client->jobPeek($jobId);
$this->assertEquals($jobId, $job->getId());

CACHE

• The cache backend adapters now return boolean when calling Phalcon\Cache\BackendInterface::save

// Returns true/false
$result = $backendCache->save('my_key', $content);

• Added Phalcon\Cache\Frontend\Msgpack. MsgPack is a new frontend cache. It is an efficient binary serialization format, which allows exchanging data among multiple languages like JSON.

use Phalcon\Cache\Backend\File;
use Phalcon\Cache\Frontend\Msgpack;

// Cache the files for 2 days using Msgpack frontend
$frontCache = new Msgpack(
    [
        'lifetime' => 172800,
    ]
);

// Create the component that will cache 'Msgpack' to a 'File' backend
// Set the cache file directory - important to keep the '/' at the end of
// of the value for the folder
$cache = new File(
    $frontCache, 
    [
        'cacheDir' => '../app/cache/',
    ]
);

// Try to get cached records
$cacheKey = 'robots_order_id.cache';
$robots   = $cache->get($cacheKey);

if ($robots === null) {
    // $robots is null due to cache expiration or data do not exist
    // Make the database call and populate the variable
    $robots = Robots::find(['order' => 'id']);

    // Store it in the cache
    $cache->save($cacheKey, $robots);
}

// Use $robots
foreach ($robots as $robot) {
    echo $robot->name, "\n";
}

• Fixed bug of destroy method of Phalcon\Session\Adapter\Libmemcached

• Added Phalcon\Cache\Backend\Memcache::addServers to enable pool of servers for memcache

$memcache->addServers('10.4.6.10', 11000, true);
$memcache->addServers('10.4.6.11', 11000, true);
$memcache->addServers('10.4.6.12', 11000, true);

CRYPT

• Mcrypt is replaced with openssl in Phalcon\Crypt 1153011486 Due to the lack of updates for mcrypt for a number of years, its slow performance and the fact that the PHP core team decided to deprecate mcrypt as soon as possible (version 7.1 onward), we have replaced it with the much faster and supported openssl.

• Default encrypt algorithm in Phalcon\Crypt is now changed to AES-256-CFB

• Removed methods setMode(), getMode(), getAvailableModes() in Phalcon\CryptInterface (no longer apply with openssl)

BACKWARDS INCOMPATIBLE: Backwards compatibility from openssl to mcrypt is problematic if not impossible. We had to remove several methods that are no longer applicable. Additionally the rijndael-256 from mcrypt is no longer valid in openssl. The default encryption algorithm is AES-256-CFB

If you have data that has already been encrypted with mcrypt, you will need first to decrypt it before upgrading to 3.0 and then encrypt it again using 3.0 and therefore openssl. Failure to do so will result in loss of data. A port is available in the incubator. Please see the code here

DATABASE

• Dropped support of Oracle 1200812009 Support of Oracle has been dropped from the Phalcon Core for the following reasons: •• The lack of Oracle maintainer •• The lack of relevant experience among the Phalcon Core Team •• Weak support or interest from the community •• Incomplete implementation that creates only the illusion of support for Oracle •• Some issues hampering for the support of PHP 7 in Phalcon

Oracle components will be ported to the Phalcon Incubator. If the adapter receives support and enhancements from the community, we will consider making it part of the core again.

DI

Phalcon\Di is now bound to services closures allowing use Phalcon\Di as $this to access services within them. Additionally, closures used as handlers in Mvc\Micro are now bound to the $app instance

Old way:

$diContainer->setShared(
    'modelsCache',
    function () use ($config) {
        $frontend = '\Phalcon\Cache\Frontend\\' . $config->get('modelsCache')->frontend;
        $frontend = new $frontend(
            [
                'lifetime' => $config->get('modelsCache')->lifetime,
            ]
        );
        $config   = $config->get('modelsCache')->toArray();
        $backend  = '\Phalcon\Cache\Backend\\' . $config['backend'];

        return new $backend($frontend, $config);
    }
);

New way:

$diContainer->setShared(
    'modelsCache',
    function () {
        $frontend = '\Phalcon\Cache\Frontend\\' . $this->config->get('modelsCache')->frontend;
        $frontend = new $frontend(
            [
                'lifetime' => $this->config->get('modelsCache')->lifetime,
            ]
        );
        $config   = $this->config->get('modelsCache')->toArray();
        $backend  = '\Phalcon\Cache\Backend\\' . $config['backend'];

        return new $backend($frontend, $config);
    }
);

Also note the nested DI behavior:

$foo = function() {
    get_class($this); // DI
    $bar = function () {
        get_class($this); // DI
        $baz = function () {
            // etc
        }
    }
}

• If an object is returned after firing the event beforeServiceResolve in Phalcon\Di it overrides the default service localization process

DISPATCHER

• Added Phalcon\Dispatcher::hasParam().

public function testAction() 
{    
    if (true === $this->dispatcher->hasParam('foo')) {
        // Parameter exists
    }
}

• Added method getActionSuffix() in Phalcon\DispatcherInterface. This allows you change the ‘Action’ suffix in controller actions.

• Corrected behavior to fire the dispatch:beforeException event when there is any exception during dispatching 11458

• CLI parameters are now handled consistently.

• Added Phalcon\Mvc\Controller\BindModelInterface and associated model type hint loading through dispatcher.

• Added Phalcon\Mvc\Collection::update, Phalcon\Mvc\Collection::create and Phalcon\Mvc\Collection::createIfNotExist

public function createAction() 
{
    /**
     * Creates a document based on the values in the attributes, if not found by criteria
     */
    $robot = new Robot();
    $robot->name = 'MyRobot';
    $robot->type = 'Droid';
    $robot->create();
}

public function createOverrideAction() 
{
    /**
     * Create a document
     */
    $robot = new Robot();
    $robot->name = 'MyRobot';
    $robot->type = 'Droid';
    //create only if robot with same name and type does not exist
    $robot->createIfNotExist( array( 'name', 'type' ) );
}

public function updateAction() 
{
    /**
     * Update a document
     */
    $robot = Robots::findFirst(['id' => 1]);
    $robot->name = 'MyRobot';
    $robot->type = 'Droid';
    $robot->update();
}

EVENTS

• Now Phalcon\Events\Event implements Phalcon\Events\EventInterface

Phalcon\Events\Event::getCancelable renamed to Phalcon\Events\Event::isCancelable

BACKWARDS INCOMPATIBLE: Any references to getCancelable will stop working. You will need to rename the function to isCancelable

Old way:

public function cancelAction()
{
    if (true === $this->eventsManager->getCancelable()) {
        // do something here
    }
}

New way:

public function cancelAction()
{
    if (true === $this->eventsManager->isCancelable()) {
        // do something here
    }
}

• Removed Phalcon\Events\Manager::dettachAll in favor of Phalcon\Events\Manager::detachAll

BACKWARDS INCOMPATIBLE: Any references to dettachAll will stop working. You will need to rename the function to detachAll

Old way:

public function destroyAction()
{
    $this->eventsManager->dettachAll()
}

New way:

public function destroyAction()
{
    $this->eventsManager->detachAll()
}

FLASH

• Added ability to autoescape Flash messages 11448

$flash = new Phalcon\Flash\Session;
$flash->setEscaperService(new Phalcon\Escaper);

$flash->success("<script>alert('This will execute as JavaScript!')</script>");
echo $flash->output();
// <div class="successMessage">&lt;script&gt;alert(&#039;This will execute as JavaScript!&#039;)&lt;/script&gt;</div>

• Fixed Phalcon\Session\Flash::getMessages. Now it returns an empty array in case of non existent message type request 11941

Old result:

use Phalcon\Session\Flash as FlashSession;

$flash = new FlashSession();
$flash->error('Error Message');
var_dump($flash->getMessages('success', false));

array (size=1)
  'error' => 
    array (size=1)
      0 => string 'Error Message' (length=13)

New result:

use Phalcon\Session\Flash as FlashSession;

$flash = new FlashSession();
$flash->error('Error Message');
var_dump($flash->getMessages('success', false));

array (size=0)
  empty

HTTP REQUEST/RESPONSE

• Added default header: Content-Type: "application/json; charset=UTF-8" in method Phalcon\Http\Response::setJsonContent

Old way:

use Phalcon\Http\Response;

$data     = 'Phlying with Phalcon';
$response = new Response();
$response->setContentType('application/json;');
$response->setJsonContent($data)
$response->send();

New way:

$data     = 'Phlying with Phalcon';
$response = new Response();
$response->setJsonContent($data)
$response->send();

• Added ability to spoof the HTTP request method. Most browsers do not support sending PUT and DELETE requests via the method attribute in an HTML form. If the X-HTTP-Method-Override header is set, and if the method is a POST, then it is used to determine the ‘real’ intended HTTP method. The _method request parameter can also be used to determine the HTTP method, but only if setHttpMethodParameterOverride(true) has been called. By including a _method parameter in the query string or parameters of an HTTP request, Phalcon will use this as the method when matching routes. Forms automatically include a hidden field for this parameter if their submission method is not GET or POST.

• Added support of CONNECT, TRACE and PURGE HTTP methods.

  • CONNECT: A variation of HTTP tunneling when the originating request is behind a HTTP proxy server. With this mechanism, the client first requests the HTTP proxy server to forward the TCP connection to the final endpoint. The HTTP proxy server then establishes the connection on behalf of the client.
  • TRACE: A method used for debugging which echoes input back to the user. Note that this method is dangerous, since it introduces a risk whereby an attacker could steal information such as cookies and possibly server credentials.
  • PURGE: Although not defined in the HTTP RFCs, some HTTP servers and caching systems implement this method and use it to purge cached data.

• Refactored Phalcon\Http\Request::getHttpHost. Now it always returns the hostname or empty an string. Optionally validates and cleans host name 257311921

• Renamed Phalcon\Http\Request::isSoapRequest to Phalcon\Http\Request::isSoap and Phalcon\Http\Request::isSecureRequest to Phalcon\Http\Request::isSecure. Left the originals functions as aliases and marked them deprecated.

CAUTION: Any references to isSoapRequest need to be renamed to isSoap. Any references to isSecureRequest need to be renamed to isSecure.

Old way:

public function testAction()
{
    if (true === $this->request->isSoapRequest()) {
        //
    }

    if (true === $this->request->isSecureRequest()) {
        //
    }
}

New way:

public function testAction()
{
    if (true === $this->request->isSoap()) {
        //
    }

    if (true === $this->request->isSecure()) {
        //
    }
}

• Added Phalcon\Http\Request::setStrictHostCheck and Phalcon\Http\Request::isStrictHostCheck to manage strict validation of the host name.

use Phalcon\Http\Request;

$request = new Request;

$_SERVER['HTTP_HOST'] = 'example.com';
$request->getHttpHost(); // example.com

$_SERVER['HTTP_HOST'] = 'example.com:8080';
$request->getHttpHost(); // example.com:8080

$request->setStrictHostCheck(true);
$_SERVER['HTTP_HOST'] = 'ex=am~ple.com';
$request->getHttpHost(); // UnexpectedValueException

$_SERVER['HTTP_HOST'] = 'ExAmPlE.com';
$request->getHttpHost(); // example.com

• Added Phalcon\Http\Request::getPort. Returns the port on which the request is made i.e. 80, 8080, 443 etc.

• Added setLastModified method to Phalcon\Http\Response Sets the Last-Modified header

public function headerAction()
{
    $this->response->setLastModified(new DateTime());
}

• Add setContentLength method to Phalcon\Http\Response Sets the response content-length

public function headerAction()
{
    $this->response->setContentLength(2048);
}

LOADER

• Removed support for prefixes strategy in Phalcon\Loader

BACKWARDS INCOMPATIBLE: In Phalcon 2, you could load classes using a specific prefix. This method was very popular before namespaces were introduced. For instance:

<?php

use Phalcon\Loader;

// Creates the autoloader
$loader = new Loader();

// Register some prefixes
$loader->registerPrefixes(
    array(
        "Example_Base"    => "vendor/example/base/",
        "Example_Adapter" => "vendor/example/adapter/",
        "Example_"        => "vendor/example/"
    )
);

// Register autoloader
$loader->register();

// The required class will automatically include the
// file vendor/example/adapter/Some.php
$some = new Example_Adapter_Some();

This functionality is no longer supported

• Added \Phalcon\Loader::registerFiles and \Phalcon\Loader::getFiles. registerFiles registers files that are “non-classes” hence need a “require”. This is very useful for including files that only have functions. getFiles returns the files currently registered in the autoloader

$loader->registerFiles(
    [
        'fuctions.php',
        'arrayFunctions.php',
    ]
);

MODELS

• Changed constructor of Phalcon\Mvc\Model to allow pass an array of initialization data

$customer = new Customer(
    [
        'Name'   => 'Peter',
        'Status' => 'active',
    ]
);
$customer->save();

Phalcon\Mvc\Model now implements JsonSerializable making easy serialize model instances

$customers = Customers::find();
echo json_encode($customers); // {['id':1,...],['id':2,...], ...}

Phalcon\Mvc\Model\Criteria::getOrder renamed to Phalcon\Mvc\Model\Criteria::getOrderBy

BACKWARDS INCOMPATIBLE: Any references to getOrder will stop working. You will need to rename the function to getOrderBy

• Added method getOption() in Phalcon\Mvc\Model\RelationInterface Returns an option by the specified name. If the option does not exist null is returned

• Added OR operator for Phalcon\Mvc\Model\Query\Builder methods: betweenWhere, notBetweenWhere, inWhere and notInWhere

$builder->betweenWhere('price', 100.25, 200.50);     // Appends a BETWEEN condition
$builder->notBetweenWhere('price', 100.25, 200.50);  // Appends a NOT BETWEEN condition
$builder->inWhere('id', [1, 2, 3]);                  // Appends an IN condition
$builder->notInWhere('id', [1, 2, 3]);               // Appends an NOT IN condition

• Added new getter Phalcon\Mvc\Model\Query\Builder::getJoins() Returns the join parts from query builder

• When destructing a Mvc\Model\Manager PHQL cache is cleaned

• Added FULLTEXT index type to Phalcon\Db\Adapter\Pdo\Mysql

• Fixed afterFetch event not being sent to behaviors

• Fixed issue with Model::__set that was bypassing setters 11286

• Fixed issue with Model::__set setting hidden attributes directly when setters are not declared 11286

Phalcon\Mvc\Model\Manager::load() now can load models from aliased namespaces

Phalcon\Mvc\Model\Transaction\Manager now correctly keeps account of transactions 11554

Phalcon\Db\Dialect\Sqlite now maps additional column types to SQLite columns equivalents.

• Fixed Phalcon\Mvc\Model\Resultset::update() - Removed endless loop queries

• Fixed Phalcon\Mvc\Model\Manager::_mergeFindParameters - Merging conditions fix

ROLES

• Added Phalcon\Acl\RoleAware and Phalcon\Acl\ResourceAware Interfaces. Now you can pass objects to Phalcon\Acl\AdapterInterface::isAllowed as roleName and resourceName, also they will be automatically passed to function defined in Phalcon\Acl\AdapterInterface::allow or Phalcon\Acl\AdapterInterface::deny by type

use UserRole;       // Class implementing RoleAware interface
use ModelResource;  // Class implementing ResourceAware interface

// Set access level for role into resources
$acl->allow('Guests', 'Customers', 'search');
$acl->allow('Guests', 'Customers', 'create');
$acl->deny('Guests', 'Customers', 'update');

// Create our objects providing roleName and resourceName
$customer     = new ModelResource(1, 'Customers', 2);
$designer     = new UserRole(1, 'Designers');
$guest        = new UserRole(2, 'Guests');
$anotherGuest = new UserRole(3, 'Guests');

// Check whether our user objects have access to the operation on model object
$acl->isAllowed($designer, $customer, 'search')     // Returns false
$acl->isAllowed($guest, $customer, 'search')        // Returns true
$acl->isAllowed($anotherGuest, $customer, 'search') // Returns true

Phalcon\Acl\AdapterInterface::allow and Phalcon\Acl\AdapterInterface::deny have 4th argument - function. It will be called when using Phalcon\Acl\AdapterInterface::isAllowed

Phalcon\Acl\AdapterInterface::isAllowed have 4th argument - parameters. You can pass arguments for a function defined in Phalcon\Acl\AdapterInterface:allow or Phalcon\Acl\AdapterInterface::deny as associative array where key is argument name

// Set access level for role into resources with custom function
$acl->allow(
    'Guests', 
    'Customers', 
    'search',
    function ($a) {
        return $a % 2 == 0;
    }
);

// Check whether role has access to the operation with custom function
$acl->isAllowed('Guests', 'Customers', 'search', ['a' => 4]); // Returns true
$acl->isAllowed('Guests', 'Customers', 'search', ['a' => 3]); // Returns false

• Fixed wildcard inheritance in Phalcon\Acl\Adapter\Memory 1200412006

use Phalcon\Acl;
use Phalcon\Acl\Adapter\Memory as MemoryAcl;

$acl = new MemoryAcl();

$acl->setDefaultAction(Acl::DENY);

$roleGuest      = new Role("guest");
$roleUser       = new Role("user");
$roleAdmin      = new Role("admin");
$roleSuperAdmin = new Role("superadmin");

$acl->addRole($roleGuest);
$acl->addRole($roleUser, $roleGuest);
$acl->addRole($roleAdmin, $roleUser);
$acl->addRole($roleSuperAdmin, $roleAdmin);

$acl->addResource("payment", ["paypal", "facebook",]);

$acl->allow($roleGuest->getName(), "payment", "paypal");
$acl->allow($roleGuest->getName(), "payment", "facebook");

$acl->allow($roleUser->getName(), "payment", "*");

echo $acl->isAllowed($roleUser->getName(), "payment", "notSet");  // true
echo $acl->isAllowed($roleUser->getName(), "payment", "*");       // true
echo $acl->isAllowed($roleAdmin->getName(), "payment", "notSet"); // true
echo $acl->isAllowed($roleAdmin->getName(), "payment", "*");      // true

ROUTES

• Routes now can have an associated callback that can override the default dispatcher + view behavior

• Amended Phalcon\Mvc\RouterInterface and Phalcon\Mvc\Router. Added missed addPurge, addTrace and addConnect methods. Added addConnect for the CONNECT HTTP method, addPurge for the PURGE HTTP method and addTrace for the TRACE HTTP method

• Placeholders :controller and :action in Mvc\Router now defaults to /([\\w0-9\\_\\-]+) instead of /([\\a-zA-Z0-9\\_\\-]+)

• Modifier #u (PCRE_UTF8) is now default in regex based routes in Mvc\Router

Mvc\Router\Route now escapes characters such as . or + to avoid unexpected behaviors

• Fixed the use of the annotation router with namespaced controllers

• Fixed matching host name by Phalcon\Mvc\Route::handle when using port on current host name 2573

SECURITY

• Added Phalcon\Security::hasLibreSsl and Phalcon\Security::getSslVersionNumber Mostly these are used internally but can be used to get information about libreSsl.

• Changed default hash algorithm in Phalcon\Security to CRYPT_BLOWFISH_Y

Phalcon\Security is using now Phalcon\Security\Random

• Enforced that Phalcon\Security::getToken() and Phalcon\Security::getTokenKey() return a random value per request not per call

Phalcon\Security::getToken() and Phalcon\Security::getTokenKey() are using now Phalcon\Security::_numberBytes instead of passed as argument or hard coded value

Phalcon\Security::hash() corrected not working CRYPT_STD_DES, CRYPT_EXT_DES, MD5, CRYPT_SHA256

Phalcon\Security::hash() CRYPT_SHA512 fixed wrong salt length

• Added missing unit-tests for Phalcon\Security

SESSION

• Removed Phalcon\Session 11340

BACKWARDS INCOMPATIBLE: Any references to Phalcon\Session have to be removed and replaced with the relevant adapter class

• Fixed the Session write callback 11733

TEXT

• Added ability to use custom delimiter for Phalcon\Text::camelize and Phalcon\Text::uncamelize 10396

use Phalcon\Text;
        
public function displayAction()
{
    echo Text::camelize('c+a+m+e+l+i+z+e', '+'); // CAMELIZE
}

• Fixed Phalcon\Text:dynamic() to allow custom separator 11215

VIEW

• An absolute path can now be used to Mvc\View::setLayoutsDir You can now use one layout path for all the landing pages of your application for instance, even from separate projects

• Now Phalcon\Mvc\View supports many views directories at the same time

• Return false from an action disables the view component (same as $this->view->disable())

public function displayAction()
{
    // Do some stuff here
    
    return false; // Same as $this->view->disable();
}

• Return a string from an action takes it as the body of the response

• Return a string from an Mvc\Micro handler takes it as the body of the response

public function displayAction()
{
    // Do some stuff here
    
    // $this->response->setContent('<h1>Hello World</h1>');
    return '<h1>Hello World</h1>';
}

• Fixed odd view behavior 1933 related to setLayout() and pick()

VALIDATION

Phalcon\Mvc\Model\Validation is now deprecated in favor of Phalcon\Validation The functionality of both components is merged into one, allowing us to reduce the codebase while offering the same functionality as before.

Old way:

namespace Invo\Models;

use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Email as EmailValidator;
use Phalcon\Mvc\Model\Validator\Uniqueness as UniquenessValidator;

class Users extends Model
{
    public function validation()
    {
        $this->validate(
            new EmailValidator(
                [
                    'field' => 'email',
                ]
            )
        );

        $this->validate(
            new UniquenessValidator(
                [
                    'field'   => 'username',
                    'message' => 'Sorry, That username is already taken',
                ]
            )
        );

        if ($this->validationHasFailed() == true) {
            return false;
        }
    }
}

New way:

namespace Invo\Models;

use Phalcon\Mvc\Model;
use Phalcon\Validation;
use Phalcon\Validation\Validator\Email as EmailValidator;
use Phalcon\Validation\Validator\Uniqueness as UniquenessValidator;

class Users extends Model
{
    public function validation()
    {
        $validator = new Validation();

        $validator->add(
            'email', //your field name
            new EmailValidator([
                'model' => $this,
                'message' => 'Please enter a correct email address'
            ])
        );

        $validator->add(
            'username',
            new UniquenessValidator([
                'model' => $this,
                'message' => 'Sorry, That username is already taken',
            ])
        );

        return $this->validate($validator);
    }
}

• Method isSetOption in Phalcon\Validation\ValidatorInterface marked as deprecated, please use hasOption

CAUTION: Any references to isSetOption need to be renamed to hasOption

Old way:

if (true === $validation->isSetOption('my-option')) {
    //
}

New way:

if (true === $validation->hasOption('my-option')) {
    //
}

• Added internal check allowEmpty before calling a validator. If it option is true and the value of empty, the validator is skipped

• Added option to validate multiple fields with one validator (fix uniqueness validator as well), also removes unnecessary model => $this in Phalcon\Validation\Validator\Uniqueness.

Phalcon\Validation\Validator\Alpha now correctly validates non-ASCII characters 11386

• Added Phalcon\Validation\CombinedFieldsValidator, validation will pass array of fields to this validator if needed

Phalcon\Validation\Validator\Digit now correctly validates digits 11374

use Phalcon\Validation\Validator\Digit as DigitValidator;

$validator->add(
    'height', 
    new DigitValidator(
        [
            'message' => ':field must be numeric',
        ]
    )
);

$validator->add(
    [
        'height', 
        'width',
    ], 
    new DigitValidator(
        [
            'message' => [
                'height' => 'height must be numeric',
                'width'  => 'width must be numeric',
            ]
        ]
    )
);

• Added Phalcon\Validation\Validator\Date

use Phalcon\Validation\Validator\Date as DateValidator;

$validator->add(
    'date', 
    new DateValidator(
        [
            'format'  => 'd-m-Y',
            'message' => 'The date is not valid',
        ]
    )
);

$validator->add(
    [
        'date',
        'anotherDate',
    ], 
    new DateValidator(
        [
            'format'  => [
                'date'        => 'd-m-Y',
                'anotherDate' => 'Y-m-d',
            ],
            'message' => [
                'date'        => 'The date is invalid',
                'anotherDate' => 'The another date is invalid',
            ]
        ]
    )
);

• Fixed Phalcon\Validation::appendMessage to allow append message to the empty stack 10405

• Added convert option to the Phalcon\Validation\Validator\Uniqueness to convert values to the database lookup 1200512030

use Phalcon\Validation\Validator\Uniqueness;

$validator->add(
    'username', 
    new Uniqueness(
        [
            'convert' => function (array $values) {
                $values['username'] = strtolower($values['username']);
                
                return $values;
            }
        ]
    )
);

INTERFACES

• Removed __construct from all interfaces 1141011441

• Added Phalcon\Cli\DispatcherInterface, Phalcon\Cli\TaskInterface, Phalcon\Cli\RouterInterface and Phalcon\Cli\Router\RouteInterface.

DOCUMENTATION

• Added Indonesian translation 840

VARIOUS

• Added Phalcon\Assets\Manager::exists() to check if collection exists

• Fixed Filter::add method handler 11581

• Fixed issue with radio not being checked when default value is 0 11358

• Phalcon\Tag::getTitle() shows a title depending on prependTitle and appendTitle

• Using a settable variable for the Mongo Connection Service name instead of a hard coded string 11725

Phalcon\Debug\Dump skip debugging di, fix detecting private/protected properties

• Added new setter Phalcon\Escaper::setDoubleEncode() - to allow setting/disabling double encoding

• Fixed Phalcon\Config::merge for working with php7

PHP7

Phalcon 3.0 supports PHP7! In subsequent releases we will focus on the development of the framework to improve the compatibility and take advantage of the performance enhancements that PHP7 offers. You can install the framework in php7 using the usual installation instructions.

Support

Phalcon 3.0 Long Term Support (LTS) version is out, and it’s packed with new features to help you better create web applications with PHP. This version of the framework will be maintained for 3 years from now.

Acknowledgments

We want to greatly thank everyone who has contributed to accomplish and achieve the completion of this release. Special thanks to our friends around the world that have made possible this release:

Conclusion

Phalcon 3.0 takes a step forward towards a modern framework for PHP. We’ll continue working making it more useful and performant for developers. Thank you once more to our wonderful community and users!

Installation

You can install Phalcon 3.0 for either PHP 5.5/5.6/7.0 using the following instructions:

git clone --depth=5 https://github.com/phalcon/cphalcon
cd cphalcon/build
sudo ./install

Windows DLLs are available in the download page.

As always, many thanks to everyone involved in this release and thanks for choosing Phalcon!


Chat - Q&A

Support

Social Media

Videos

<3 Phalcon Team

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.