Testing Environment


Phalcon has always had a small development community and not that many pull requests, offering bug fixes and enhancements, compared to other PHP frameworks. This was primarily due to the fact that most developers do not really know C. To help developers contribute, we have created a new language called Zephir, which has a very similar syntax to PHP or Javascript. In 2003, we announced this plan and a few months later we released the language and rewrote all the Phalcon code in Zephir. We have been using Zephir ever since for developing Phalcon.

The Problem

Having a framework that is rich in features requires a development environment that will offer all those features and related services. For instance one needs to install MySQL, Postgresql and Sqlite to be able to check whether functionality in the ORM will be the same when using any of these adapters for your database needs. Additionally, the relevant extensions for PHP have to be installed in the development system.

When looking at all the functionality that Phalcon offers, just to run the testing suite, one needs a great number of extensions as well as services installed (Redis, Memcached etc.)

If one considers the PHP version also (PHP 7.4, 8.0 etc.), developing for Phalcon is not an easy task, because of all these prerequisites.


We had a good solution with nanobox in the past but since that project has been discontinued, we redoubled our efforts and used docker for our needs. With a few commands, developers can be contributing to phalcon as well as running the tests in no time.


You will first need to have docker installed on your machine. Instructions on how to do that, can be found here. Additionally, we will need docker-compose. Installation instructions can be found here

Running the Environment

Fork the Repository

Fork the cphalcon to your GitHub account, if you have not done so already. Visit the cphalcon page on your browser and click the Fork button at the top right of the screen.

Clone the Fork

Now you will need to clone the forked repository to a folder of your choice. The example below assumes that the GitHub account is niden - change it to your own.

git clone [email protected]:niden/cphalcon

Build the environment

Once you are in cphalcon folder (or wherever you cloned the repository), you will need to build the containers

docker-compose build 

This will be a lengthy process, depending on the specifications of your machine. This process is not run frequently, only when there are changes in the dockerfiles.

Start the environment

Once all the containers have been built, you will need to start it. You can start it with the containers exposing ports to your host or without.

docker-compose up -d

The above command uses the docker-compose.yml file from the repository. The -d command runs the environment in the background, and you can reuse your terminal. Without this option, you will have to use Ctrl-C to stop it.

With the above command, the services containers will bind their respective ports to your host.

Service Port
mysql 3306
postgres 5432
redis 6379

You can then connect to your environment from your host directly. For example to connect to the mysql database, you will just need localhost as your host, since the 3306 port is bound.

This configuration is very handy and works well for most developers. However, there are developers that work on many projects at the same time, and those projects use the same services (i.e. mysql). Using this configuration will not allow a second environment to work, that uses mysql in the same manner, because the mysql port on the host is already in use.

You can therefore, use the docker-compose-local.yml file, which does not expose ports from the services containers to the host, keeping everything isolated.

docker-compose -f docker-compose-local.yml up -d

If you use the above command to start your environment, you will need to know the IP address of a service container that you need to connect to. If, for instance, you need to connect to the mysql container, using localhost as your host will not work. You will need to find the correct IP address:

docker inspect cphalcon-mysql

cphalcon-mysql is the name for the mysql service. You can check the docker-compose-local.yml file if you are interested in finding the names of the containers. The above command will produce:

        "Id": "121513ec37c31bcb717526b5f792e373534a9d7187db5d919d30e8c89a7cc897",
        "Created": "2022-09-01T16:05:38.440859071Z",
        "Path": "",
        "Args": [
        "NetworkSettings": {
            "Networks": {
                "cphalcon_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                    "NetworkID": "3be8b1ff3f87e11bd60c568d3a5ca04d0cd6b07779bcbe585eaac12f60bf26c9",
                    "EndpointID": "d0940c441edf26b573e2f42f7f659666c4c1535394843bc0e115ddbac947420b",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:12:00:04",
                    "DriverOpts": null

Then you can connect to the container using the IPAddress

mysql -uroot -p -h172.18.0.4

Stop the environment

To stop the environment you can press Ctrl-C if you have not used the -d flag. If you have, you will need to tell docker that you no longer wish the environment to be up:

docker-compose down

Enter the Environment

To enter the environment, we will need to tell docker, which one we need. There are three environments built for us:

  • cphalcon-7.4
  • cphalcon-8.0
  • cphalcon-8.1

Each of those represents the PHP version that they have installed.

To enter the PHP 8.1 environment, we need:

docker exec -it cphalcon-8.1 /bin/bash

The following prompt will appear:


You are now inside the environment with all the extensions and services you need.

To exit the environment, all you need is to type exit and press the enter key.

root@cphalcon-81:/srv# exit 


The environments come with predefined aliases for your terminal. You can find them all in the .bashrc file under the docker/ folder and relevant PHP version subfolder. Some of these are:

Alias Command
g git
h history
l ls -lF ${colorflag}
ll LC_ALL=”C.UTF-8” ls -alF
zephir ./zephir
zf ./zephir fullclean
zg ./zephir generate
zs ./zephir stubs
cpl zf && zg && cd ext/ && ./install && ..
codecept php -d extension=ext/modules/ ./vendor/bin/codecept
phpcs php -d extension=ext/modules/ ./vendor/bin/phpcs
phpcbf php -d extension=ext/modules/ ./vendor/bin/phpcbf
psalm php ./vendor/bin/psalm
test-unit Run all unit tests
test-cli Run all cli tests
test-integration Run all integration tests
test-db-common Run all common database tests
test-db-mysql Run all mysql tests
test-db-pgsql Run all postgesql tests
test-db-sqlite Run all sqlite tests
test-db Run all database tests
test-all Run all tests


Just in case update composer:

root@cphalcon-81:/srv# composer install

Check Zephir

Zephir is already installed in the environment. Just check it:

root@cphalcon-81:/srv# zephir 

A screen like the one below should appear (output formatted for reading):

 _____              __    _
/__  /  ___  ____  / /_  (_)____
  / /  / _ \/ __ \/ __ \/ / ___/
 / /__/  __/ /_/ / / / / / /
/____/\___/ .___/_/ /_/_/_/

Zephir 0.16.2 by Andres Gutierrez and Serghei Iakovlev (3e961ab)

  command [options] [arguments]

      --dumpversion  Print the version of the compiler and don't 
                     do anything else (also works with a single hyphen)
  -h, --help         Print this help message
      --no-ansi      Disable ANSI output
  -v, --verbose      Displays more detail in error messages from 
                     exceptions generated by commands (can also disable with -V)
      --vernum       Print the version of the compiler as integer
      --version      Print compiler version information and quit

Available commands:
  api        Generates a HTML API based on the classes exposed in the extension
  build      Generates/Compiles/Installs a Zephir extension
  clean      Cleans any object files created by the extension
  compile    Compile a Zephir extension
  fullclean  Cleans any object files created by the extension 
             (including files generated by phpize)
  generate   Generates C code from the Zephir code without compiling it
  help       Display help for a command
  init       Initializes a Zephir extension
  install    Installs the extension in the extension directory 
             (may require root password)
  stubs      Generates stubs that can be used in a PHP IDE

Compile Phalcon

Phalcon is not compiled yet. We need to instruct Zephir to do that:

root@cphalcon-81:/srv# cpl

Check Extensions


root@cphalcon-81:/srv# php -m

and you will see:

[PHP Modules]

[Zend Modules]

Once you see phalcon in the list, you have the extension compiled and ready to use.

Setup databases

First, we need to have a .env file in the project root.

root@cphalcon-81:/srv# cp tests/_config/.env.docker .env

Running Tests


Now that the environment is set up, we need to run the tests. The testing framework Phalcon uses is Codeception. For a basic introduction you can check this page. Also, for the list of the commands, you can check here.

We need to first build the Codeception base classes. This needs to happen every time new functionality is introduced in Codeception’s helpers.

Now you can run:

root@cphalcon-81:/srv# codecept build

The output should show:

Building Actor classes for suites: cli, database, integration, unit
 -> CliTesterActions.php generated successfully. 152 methods added
\CliTester includes modules: Asserts, Cli, \Helper\Cli, \Helper\Unit
 -> DatabaseTesterActions.php generated successfully. 252 methods added
\DatabaseTester includes modules: Phalcon4, Redis, Asserts, Filesystem, Helper\Database, Helper\Unit
 -> IntegrationTesterActions.php generated successfully. 251 methods added
\IntegrationTester includes modules: Phalcon4, Redis, Asserts, Filesystem, Helper\Integration, Helper\PhalconLibmemcached, Helper\Unit
 -> UnitTesterActions.php generated successfully. 166 methods added
\UnitTester includes modules: Apc, Asserts, Filesystem, Helper\Unit

Now we can run the tests:

root@cphalcon-81:/srv# test-unit

This will start running the unit testing suite. You will see a lot of tests and assertions. At the time of this article, we have Tests: 2780, Assertions: 8965, Skipped: 34 unit tests. The reason for so many skipped tests is that we created test stubs for every component and every method in each component. This was to create awareness on what needs to be checked and what components/methods we need to write tests for. Of course some test stubs are duplicate or obsolete. Those will be deleted once the relevant component is checked and tests written for it. Our goal is to get as close to 100% code coverage as possible. If we manage to get to 100% that would be great!

Execute all tests from a folder:

root@cphalcon-81:/srv# codecept run tests/unit/some/folder/

Execute single test:

root@cphalcon-81:/srv# codecept run tests/unit/some/folder/some/test/file.php


To run database related tests you can use the relevant aliases:

root@cphalcon-81:/srv# test-db-common
root@cphalcon-81:/srv# test-db-mysql 
root@cphalcon-81:/srv# test-db-pgsql 
root@cphalcon-81:/srv# test-db-sqlite
root@cphalcon-81:/srv# test-db       


You can now open your favorite editor and start developing in Zephir. You can create new functionality, fix issues, write tests etc. Remember though that if you change any of the zep files (inside the phalcon folder), you will need to recompile the extension:

root@cphalcon-81:/srv# cpl

and then you can run your tests

root@cphalcon-81:/srv# codecept run tests/unit/somefolder/somecestfile:sometest

For Zephir documentation, you can visit the Zephir Docs site.


The available services are:

  • Memcached
  • Mysql
  • Postgresql
  • Redis

The PHP extensions enabled are:

  • apcu
  • ctype
  • curl
  • dom
  • fileinfo
  • gd
  • gmp
  • gettext
  • imagick
  • iconv
  • igbinary
  • intl
  • json
  • memcached
  • mbstring
  • mongodb
  • opcache
  • phar
  • pdo
  • pdo_mysql
  • pdo_pgsql
  • pdo_sqlite
  • redis
  • session
  • simplexml
  • sqlite3
  • tokenizer
  • yaml
  • zephir_parser
  • xdebug
  • xml
  • xmlwriter
  • zip
  • zlib

The database dumps are located under tests/_data/assets/schemas

If you have any questions, feel free to join us in our Discord server or our Discussions.

<3 Phalcon Team