# PHP Frameworks

The information below contains information specific to PHP. 
For further guidance, review the 
[documentation](https://frameworkbenchmarks.readthedocs.io/en/latest/).

## Infrastructure Software Versions

### PHP Versions

[PHP 7.2.2](https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/toolset/setup/linux/languages/php/php7.sh),
[PHP 5.6.33](https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/toolset/setup/linux/languages/php/php5.sh) and [HHVM 3.24](https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/toolset/setup/linux/languages/hhvm.sh).

## Adding New PHP-based Frameworks

### PHP Acceleration and Caching

Caching the output of the PHP bytecode compiler is expressly 
allowed by this benchmark. As we use PHP 5.5, which comes 
with opcache built in, we recommend you use this. However, 
some frameworks utilize APC instead as switching can be 
problematic (e.g. APC allows arbitrary data caching, while 
opcache). 

Caching the output of parsing your configuration files is 
also expressly allowed (e.g. file caching, metadata caching).
Some frameworks use APCu or memcached to achieve this. 

Caching of the classloader (often referred to as optimizing
the classloader) is also allowed. Most frameworks have their 
own methods of doing this. 

*Caching of any data fetched from the database is not allowed*. 
Specifically, things such as [Doctrine's Result Cache](http://doctrine-orm.readthedocs.org/en/latest/reference/caching.html#result-cache) are inadmissible. 

However, if you are using an ORM that prepares SQL 
statements in some way, such as how 
[Doctrine](http://doctrine-orm.readthedocs.org/en/latest/reference/caching.html#query-cache) 
translates DQL into SQL, this translated form can be 
cached, as long as you are dynamically accepting 
query parameters. 

Caching any data using databases (Redis, MongoDB, etc) 
is discouraged, as 1) our databases run on a separate 
computer across the network, so you won't see much 
benefit 2) your usage of the DB *may* impact other 
framework's tests, which we cannot allow. You may launch 
a DB locally on the application server as part of your 
`setup.sh` scripts and utilize it for caching the allowable
cache items, if you so desire, but it's normally much 
easier to use systems such as APCu.

Ask if you are not certain.

## Install.sh and Setup.sh Files

Most PHP frameworks use `fw_depends php5 nginx composer` in their `install.sh` file, 
which installs PHP5, Nginx, and Composer automatically. They then create a `setup.sh`
containing these lines (comments added to clarify what's happening). `fw_depends php7` is now also available.

    # Explanation of variables provided by toolset:
    #   FWROOT - absolute path to framework toolset, normally 
    #            something like /home/username/FrameworkBenchmarks
    #   TROOT  - absolute path to this framework's folder, normally
    #            something like /home/username/FrameworkBenchmarks/frameworks/PHP/cakephp
    #   IROOT  - absolute path to the location where the toolset has installed software, 
    #            something like /home/username/FrameworkBenchmarks/installs
    #
    # As of writing fw_depends installs 5.5.17 into the installer root
    export PHP_HOME=${IROOT}/php-5.5.17
    export PHP_FPM=$PHP_HOME/sbin/php-fpm
    export NGINX_HOME=${IROOT}/nginx
    
    # Uses the full path to php-fpm
    #   - php-fpm configuration is located in the config folder found in the 
    #     root of this project
    #   - We tell PHP-FPM to place it's PID file into the deploy folder
    $PHP_FPM --fpm-config $FWROOT/toolset/setup/linux/languages/php/php-fpm.conf -g $TROOT/deploy/php-fpm.pid
    # Turn on nginx using the configuration file found in this framework's deploy
    # folder
    $NGINX_HOME/sbin/nginx -c $TROOT/deploy/nginx.conf

When using `php`, `php-fpm`, or other binaries, always use the full path 
to the binary, e.g. instead of `php <command>`, 
use `/home/foo/FrameworkBenchmarks/installs/php-5.5.17/bin/php <your command>`. 

### Dependency Management Using Composer

Many PHP apps use [Composer](https://getcomposer.org/) for dependency management, 
which greatly simplifies downloading the framework, loading the framework, and 
upgrading the framework version in the future. 

There are two main guidelines for PHP frameworks: 

* **Never include the source code for your framework into this repository.** Download it 
using Composer, wget, or some other tool
* **When using Composer, always add a `composer.lock` file in addition to the `composer.json` file**. 
The lock file is a fully-defined file generated by composer 1) reading your JSON file 2) finding all 
dependencies 3) downloading a lot of data from Github. Without this lock file, composer takes 2-3x 
longer to run, and it can even halt and require user input

#### Setting up Composer

Add a `composer.json` file to your framework's root folder, e.g. `php-fuel/composer.json`. 
Ensure your `setup.sh` lists composer as a dependency.

    # Note the order! Composer depends on PHP so it has to be defined after
    fw_depends mysql php7 nginx composer

The `composer install` command will run automatically using the following flags: `--no-dev --no-interaction --no-progress --no-suggest --optimize-autoloader --classmap-authoritative`

After the installation completes, your framework folder will have a new `vendor` folder, 
e.g. `php-fuel/vendor` that contains all dependencies. Update your PHP scripts
to either directly reference files inside of vendor, or use the `vendor/autoload.php`
file. 

#### Generating composer.lock file

Composer uses Github *a lot*, enough so that it is common for it to exceed the 
API limit and cause infinite hangs or installation failures. To avoid this, it 
is necessary to generate a `composer.lock` file. If you're lucky, you can run 
`toolset/run-tests.py --install server --test <your framework> --install-only`,
which will run your `install.sh` file and leave the `composer.lock` file right
next to your `composer.json` file. If this works, just add the lock file to 
the repository and you're done. There is more info on this [here](https://getcomposer.org/doc/03-cli.md#install)
and [here](https://circleci.com/docs/composer-api-rate-limit)

*Note:* You may have to force-add the lock file (e.g. `git add -f composer.lock`), 
because some gitignore files in this repo contain `*.lock` to avoid Ruby's lock files. 

If you are prompted for input during the `run-tests.py` script above, then you
need to generate your lock file manually so that you may answer the input 
queries as they are shown. Use these steps
    
    # Define the environment variables you need (modify as needed)
    export IROOT=/home/you/FrameworkBenchmarks/installs
    export TROOT=/home/you/FrameworkBenchmarks/frameworks/PHP/php-yii2
    
    # Run the installation shown above
    #
    # This will manually prompt you for your Github credentials 
    # to avoid the Github rate limit
    # When this command completes, you will have a lock file 
    ${IROOT}/php-5.5.17/bin/php ${COMPOSER_HOME}/composer.phar install \
      --working-dir $TROOT
    
    # Add the lock file to this repository
    git add -f composer.lock

#### Updating Composer setup

If you update `composer.json`, you need to regenerate the lock
file. If you forget to do this, you will see this error message 
when running:

    Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.

### Debugging PHP Frameworks

The first stop for HTTP 500 errors is to enable stack traces. 
Update `toolset/setup/linux/languages/php/php-fpm.conf` to include `php_flag[display_errors] = on`. 
If you don't use php-fpm, update the `config/php.ini`

## Get Help

### PHP Experts

_There aren't any PHP experts listed, yet. If you're an expert, 
add yourself!_

### Resources & Interesting Links

_If you find some interesting links related to the PHP tests, 
add them here._
