--> -->

Supported versions 

Grid Dedicated
  • 7.2
  • 7.3
  • 7.4
  • 7.2
  • 7.3
  • 7.4

Note that as of PHP 7.1 we use the Zend Thread Safe (ZTS) version of PHP.

To specify a PHP container, use the type property in your .platform.app.yaml.

type: 'php:7.4'

Deprecated versions 

The following versions are available but are not receiving security updates from upstream, so their use is not recommended. They will be removed at some point in the future.

Grid Dedicated
  • 5.4
  • 5.5
  • 5.6
  • 7.0
  • 7.1
  • 5.6
  • 7.0
  • 7.1

Support libraries 

While it is possible to read the environment directly from your application, it is generally easier and more robust to use the platformsh/config-reader Composer library which handles decoding of service credential information for you.

Alternate start commands 

PHP is most commonly run in a CGI mode, using PHP-FPM. That is the default on Platform.sh. However, you can also start alternative processes if desired, such as if you’re running an Async PHP daemon, a thread-based worker process, etc. To do so, simply specify an alternative start command in platform.app.yaml, similar to the following:

web:
    commands:
        start: php run.php
    upstream:
            socket_family: tcp
            protocol: http

The above configuration will execute the run.php script in the application root when the container starts using the PHP-CLI SAPI, just before the deploy hook runs, but will not launch PHP-FPM. It will also tell the front-controller (Nginx) to connect to your application via a TCP socket, which will be specified in the PORT environment variable. Note that the start command must run in the foreground.

If not specified, the effective default start command varies by PHP version:

  • On PHP 5.x, it’s /usr/sbin/php5-fpm.
  • On PHP 7.0, it’s /usr/sbin/php-fpm7.0.
  • On PHP 7.1, it’s /usr/sbin/php-fpm7.1-zts.
  • On PHP 7.2, it’s /usr/sbin/php-fpm7.2-zts.
  • On PHP 7.3, it’s /usr/sbin/php-fpm7.3-zts.
  • On PHP 7.4, it’s /usr/sbin/php-fpm7.4-zts.

While you can call it manually that is generally not necessary. Note that PHP-FPM cannot run simultaneously along with another persistent process (such as ReactPHP or Amp). If you need both they will have to run in separate containers.

Expanded dependencies 

In addition to the standard dependencies format, it is also possible to specify alternative repositories for use by Composer. The standard format like so:

dependencies:
    php:
        "platformsh/client": "dev-master"

is equivalent to composer require platform/client dev-master. However, you can also specify explicit require and repositories blocks:

dependencies:
    php:
        require:
            "platformsh/client": "dev-master"
        repositories:
            - type: vcs
              url: "git@github.com:platformsh/platformsh-client-php.git"

That would install platformsh/client from the alternate repository specified, as a global dependency. That is, it is equivalent to the following composer.json file:

{
    "repositories": [
        {
            "type": "vcs",
            "url":  "git@github.com:platformsh/platformsh-client-php.git"
        }
    ],
    "require": {
        "platformsh/client": "dev-master"
    }
}

That allows you to install a forked version of a global dependency from a custom repository.

Opcache preloading 

PHP 7.4 introduced a new feature called Opcache Preloading, which allows you to load selected files into shared memory when PHP-FPM starts. That means functions and classes in those files are always available and do not need to be autoloaded, at the cost of any changes to those files requiring a PHP-FPM restart. Since PHP-FPM restarts anyway when a new deploy happens this feature is a major win on Platform.sh, and we recommend using it aggressively.

To enable preloading, add a php.ini value that specifies a preload script. Any php.ini mechanism will work, but using a variable in .platform.app.yaml is the recommended approach:

variables:
    php:
        opcache.preload: 'preload.php'

The opcache.preload value is evaluated as a file path relative to the application root (where .platform.app.yaml is), and it may be any PHP script that calls opcache_compile_file(). The following example will preload all .php files anywhere in the vendor directory:

$directory = new RecursiveDirectoryIterator(getenv('PLATFORM_APP_DIR') . '/vendor');
$iterator = new RecursiveIteratorIterator($directory);
$regex = new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);

foreach ($regex as $key => $file) {
    // This is the important part!
    opcache_compile_file($file[0]);
}

FFI 

PHP 7.4 introduced support for Foreign Function Interfaces (FFI), which allows user-space code to bridge to existing C-ABI-compatible libraries. FFI is fully supported on Platform.sh.

Note: FFI is only intended for advanced use cases, and is rarely a net win for routine web requests. Use with caution.

There are a few steps to leveraging FFI:

  1. Enable the FFI extension in .platform.app.yaml:

    runtime:
        extensions:
            - ffi
    
  2. Specify a preload file in which you can call FFI::load(). Using FFI::load() in preload will be considerably faster than loading the linked library on each request or script run.

  3. Ensure the library is available locally, but not in a web-accessible directory. .so files may included in your repository, downloaded i your build hook, or compiled in your build hook. If compiling C code, gcc is available by default. If compiling Rust code, you can download the Rust compiler in the build hook .

  4. For running FFI from the command line, you will need to enable the opcache for command line scripts in addition to the preloader. The standard pattern for the command would be php -d opcache.preload="your-preload-script.php" -d opcache.enable_cli=true your-cli-script.php.

A working FFI example is available online for both C and Rust.

Debug PHP-FPM 

If you want to inspect what’s going on with PHP-FPM, you can install this small CLI :

dependencies:
  php:
    wizaplace/php-fpm-status-cli: "^1.0"

Then when you are connected to your project over SSH you can run:

$ php-fpm-status --socket=unix://$SOCKET --path=/-/status --full

Accessing services 

To access various services with PHP, see the following examples. The individual service pages have more information on configuring each service.

<?php

declare(strict_types=1);

use Elasticsearch\ClientBuilder;
use Platformsh\ConfigReader\Config;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// Get the credentials to connect to the Elasticsearch service.
$credentials = $config->credentials('elasticsearch');

try {
    // The Elasticsearch library lets you connect to multiple hosts.
    // On Platform.sh Standard there is only a single host so just
    // register that.
    $hosts = [
        [
            'scheme' => $credentials['scheme'],
            'host' => $credentials['host'],
            'port' => $credentials['port'],
        ]
    ];

    // Create an Elasticsearch client object.
    $builder = ClientBuilder::create();
    $builder->setHosts($hosts);
    $client = $builder->build();

    $index = 'my_index';
    $type = 'People';

    // Index a few document.
    $params = [
        'index' => $index,
        'type' => $type,
    ];

    $names = ['Ada Lovelace', 'Alonzo Church', 'Barbara Liskov'];

    foreach ($names as $name) {
        $params['body']['name'] = $name;
        $client->index($params);
    }

    // Force just-added items to be indexed.
    $client->indices()->refresh(array('index' => $index));


    // Search for documents.
    $result = $client->search([
        'index' => $index,
        'type' => $type,
        'body' => [
            'query' => [
                'match' => [
                    'name' => 'Barbara Liskov',
                ],
            ],
        ],
    ]);

    if (isset($result['hits']['hits'])) {
        print <<<TABLE
<table>
<thead>
<tr><th>ID</th><th>Name</th></tr>
</thead>
<tbody>
TABLE;
        foreach ($result['hits']['hits'] as $record) {
            printf("<tr><td>%s</td><td>%s</td></tr>\n", $record['_id'], $record['_source']['name']);
        }
        print "</tbody>\n</table>\n";
    }

    // Delete documents.
    $params = [
        'index' => $index,
        'type' => $type,
    ];

    $ids = array_map(function($row) {
        return $row['_id'];
    }, $result['hits']['hits']);

    foreach ($ids as $id) {
        $params['id'] = $id;
        $client->delete($params);
    }

} catch (Exception $e) {
    print $e->getMessage();
}
<?php

declare(strict_types=1);

use Platformsh\ConfigReader\Config;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// Get the credentials to connect to the Memcached service.
$credentials = $config->credentials('memcached');

try {
    // Connecting to Memcached server.
    $memcached = new Memcached();
    $memcached->addServer($credentials['host'], $credentials['port']);
    $memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);

    $key = "Deploy day";
    $value = "Friday";

    // Set a value.
    $memcached->set($key, $value);

    // Read it back.
    $test = $memcached->get($key);

    printf('Found value <strong>%s</strong> for key <strong>%s</strong>.', $test, $key);

} catch (Exception $e) {
    print $e->getMessage();
}
<?php

declare(strict_types=1);

use Platformsh\ConfigReader\Config;
use MongoDB\Client;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// The 'database' relationship is generally the name of primary database of an application.
// It could be anything, though, as in the case here here where it's called "mongodb".
$credentials = $config->credentials('mongodb');

try {

    $server = sprintf('%s://%s:%s@%s:%d/%s',
        $credentials['scheme'],
        $credentials['username'],
        $credentials['password'],
        $credentials['host'],
        $credentials['port'],
        $credentials['path']
    );

    $client = new Client($server);
    $collection = $client->main->starwars;

    $result = $collection->insertOne([
        'name' => 'Rey',
        'occupation' => 'Jedi',
    ]);

    $id = $result->getInsertedId();

    $document = $collection->findOne([
        '_id' => $id,
    ]);

    // Clean up after ourselves.
    $collection->drop();

    printf("Found %s (%s)<br />\n", $document->name, $document->occupation);

} catch (\Exception $e) {
    print $e->getMessage();
}
<?php

declare(strict_types=1);

use Platformsh\ConfigReader\Config;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// The 'database' relationship is generally the name of primary SQL database of an application.
// That's not required, but much of our default automation code assumes it.
$credentials = $config->credentials('database');

try {
    // Connect to the database using PDO.  If using some other abstraction layer you would
    // inject the values from $database into whatever your abstraction layer asks for.
    $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s', $credentials['host'], $credentials['port'], $credentials['path']);
    $conn = new \PDO($dsn, $credentials['username'], $credentials['password'], [
        // Always use Exception error mode with PDO, as it's more reliable.
        \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
        // So we don't have to mess around with cursors and unbuffered queries by default.
        \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
        // Make sure MySQL returns all matched rows on update queries including
        // rows that actually didn't have to be updated because the values didn't
        // change. This matches common behavior among other database systems.
        \PDO::MYSQL_ATTR_FOUND_ROWS => TRUE,
    ]);

    // Creating a table.
    $sql = "CREATE TABLE People (
      id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
      name VARCHAR(30) NOT NULL,
      city VARCHAR(30) NOT NULL
      )";
    $conn->query($sql);

    // Insert data.
    $sql = "INSERT INTO People (name, city) VALUES 
        ('Neil Armstrong', 'Moon'), 
        ('Buzz Aldrin', 'Glen Ridge'), 
        ('Sally Ride', 'La Jolla');";
    $conn->query($sql);

    // Show table.
    $sql = "SELECT * FROM People";
    $result = $conn->query($sql);
    $result->setFetchMode(\PDO::FETCH_OBJ);

    if ($result) {
        print <<<TABLE
<table>
<thead>
<tr><th>Name</th><th>City</th></tr>
</thead>
<tbody>
TABLE;
        foreach ($result as $record) {
            printf("<tr><td>%s</td><td>%s</td></tr>\n", $record->name, $record->city);
        }
        print "</tbody>\n</table>\n";
    }

    // Drop table
    $sql = "DROP TABLE People";
    $conn->query($sql);

} catch (\Exception $e) {
    print $e->getMessage();
}
<?php

declare(strict_types=1);

use Platformsh\ConfigReader\Config;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// The 'database' relationship is generally the name of primary SQL database of an application.
// It could be anything, though, as in the case here here where it's called "postgresql".
$credentials = $config->credentials('postgresql');

try {
    // Connect to the database using PDO.  If using some other abstraction layer you would
    // inject the values from $database into whatever your abstraction layer asks for.
    $dsn = sprintf('pgsql:host=%s;port=%d;dbname=%s', $credentials['host'], $credentials['port'], $credentials['path']);
    $conn = new \PDO($dsn, $credentials['username'], $credentials['password'], [
        // Always use Exception error mode with PDO, as it's more reliable.
        \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
        // So we don't have to mess around with cursors and unbuffered queries by default.
    ]);

    $conn->query("DROP TABLE IF EXISTS People");

    // Creating a table.
    $sql = "CREATE TABLE IF NOT EXISTS People (
      id SERIAL PRIMARY KEY,
      name VARCHAR(30) NOT NULL,
      city VARCHAR(30) NOT NULL
      )";
    $conn->query($sql);

    // Insert data.
    $sql = "INSERT INTO People (name, city) VALUES
        ('Neil Armstrong', 'Moon'),
        ('Buzz Aldrin', 'Glen Ridge'),
        ('Sally Ride', 'La Jolla');";
    $conn->query($sql);

    // Show table.
    $sql = "SELECT * FROM People";
    $result = $conn->query($sql);
    $result->setFetchMode(\PDO::FETCH_OBJ);

    if ($result) {
        print <<<TABLE
<table>
<thead>
<tr><th>Name</th><th>City</th></tr>
</thead>
<tbody>
TABLE;
        foreach ($result as $record) {
            printf("<tr><td>%s</td><td>%s</td></tr>\n", $record->name, $record->city);
        }
        print "</tbody>\n</table>\n";
    }

    // Drop table.
    $sql = "DROP TABLE People";
    $conn->query($sql);

} catch (\Exception $e) {
    print $e->getMessage();
}
<?php

declare(strict_types=1);

use Platformsh\ConfigReader\Config;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// Get the credentials to connect to the RabbitMQ service.
$credentials = $config->credentials('rabbitmq');

try {

    $queueName = 'deploy_days';

    // Connect to the RabbitMQ server.
    $connection = new AMQPStreamConnection($credentials['host'], $credentials['port'], $credentials['username'], $credentials['password']);
    $channel = $connection->channel();

    $channel->queue_declare($queueName, false, false, false, false);

    $msg = new AMQPMessage('Friday');
    $channel->basic_publish($msg, '', 'hello');

    echo "[x] Sent 'Friday'<br/>\n";

    // In a real application you't put the following in a separate script in a loop.
    $callback = function ($msg) {
        printf("[x] Deploying on %s<br />\n", $msg->body);
    };

    $channel->basic_consume($queueName, '', false, true, false, false, $callback);

    // This blocks on waiting for an item from the queue, so comment it out in this demo script.
    //$channel->wait();

    $channel->close();
    $connection->close();

} catch (Exception $e) {
    print $e->getMessage();
}
<?php

declare(strict_types=1);

use Platformsh\ConfigReader\Config;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// Get the credentials to connect to the Redis service.
$credentials = $config->credentials('redis');

try {
    // Connecting to Redis server.
    $redis = new Redis();
    $redis->connect($credentials['host'], $credentials['port']);

    $key = "Deploy day";
    $value = "Friday";

    // Set a value.
    $redis->set($key, $value);

    // Read it back.
    $test = $redis->get($key);

    printf('Found value <strong>%s</strong> for key <strong>%s</strong>.', $test, $key);

} catch (Exception $e) {
    print $e->getMessage();
}
<?php
declare(strict_types=1);

use Platformsh\ConfigReader\Config;
use Solarium\Client;

// Create a new config object to ease reading the Platform.sh environment variables.
// You can alternatively use getenv() yourself.
$config = new Config();

// Get the credentials to connect to the Solr service.
$credentials = $config->credentials('solr');

try {

    $config = [
        'endpoint' => [
            'localhost' => [
                'host' => $credentials['host'],
                'port' => $credentials['port'],
                'path' => "/" . $credentials['path'],
            ]
        ]
    ];

    $client = new Client($config);

    // Add a document
    $update = $client->createUpdate();

    $doc1 = $update->createDocument();
    $doc1->id = 123;
    $doc1->name = 'Valentina Tereshkova';

    $update->addDocuments(array($doc1));
    $update->addCommit();

    $result = $client->update($update);
    print "Adding one document. Status (0 is success): " .$result->getStatus(). "<br />\n";

    // Select one document
    $query = $client->createQuery($client::QUERY_SELECT);
    $resultset = $client->execute($query);
    print  "Selecting documents (1 expected): " .$resultset->getNumFound() . "<br />\n";

    // Delete one document
    $update = $client->createUpdate();

    $update->addDeleteById(123);
    $update->addCommit();
    $result = $client->update($update);
    print "Deleting one document. Status (0 is success): " .$result->getStatus(). "<br />\n";

} catch (Exception $e) {
    print $e->getMessage();
}

Runtime configuration 

It is possible to change the PHP-FPM runtime configuration via the runtime block on your .platform.app.yaml. The PHP-FPM options below are configurable:

  • request_terminate_timeout - The timeout for serving a single request after which the PHP-FPM worker process will be killed. That is separate from the PHP runtime’s max_execution_time ini option, which is preferred. This option may be used if the PHP process is dying without cleaning up properly and causing the FPM process to hang.

    runtime:
        request_terminate_timeout: 300
    

Project templates 

A number of project templates for major PHP applications are available on GitHub. Not all of them are proactively maintained but all can be used as a starting point or reference for building your own website or web application.

Backdrop

Backdrop

This template builds a Backdrop site, with the entire site committed to Git.

Backdrop is a PHP-based CMS, originally forked from Drupal 7.

Services:

  • PHP 7.3
  • MariaDB 10.4

View the repository on GitHub.

Deploy on Platform.sh

Basic PHP

Basic PHP

This template provides the most basic configuration for running a custom PHP project.

PHP is a high-performance scripting language especially well suited to web development.

Services:

  • PHP 7.3
  • MariaDB 10.2

View the repository on GitHub.

Deploy on Platform.sh

Drupal 8

Drupal 8

This template builds Drupal 8 using the "Drupal Recommended" Composer project. It also includes configuration to use Redis for caching, although that must be enabled post-install in `.platform.app.yaml`.

Drupal is a flexible and extensible PHP-based CMS framework.

Services:

  • PHP 7.4
  • MariaDB 10.4
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Drupal 8 Multisite

Drupal 8 Multisite

This template builds Drupal 8 in a multisite configuration using the "Drupal Recommended" Composer project. It also includes configuration to use Redis for caching, although that must be enabled post-install per-site.

Drupal is a flexible and extensible PHP-based CMS framework capable of hosting multiple sites on a single code base.

Services:

  • PHP 7.4
  • MariaDB 10.4
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Drupal 9

Drupal 9

This template builds Drupal 9 Beta using the "Drupal Recommended" Composer project. It also includes configuration to use Redis for caching, although that must be enabled post-install in `.platform.app.yaml`.

Drupal is a flexible and extensible PHP-based CMS framework.

Services:

  • PHP 7.4
  • MariaDB 10.4
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

GovCMS 8

GovCMS 8

This template builds the Australian government's GovCMS Drupal 8 distribution using the Drupal Composer project for better flexibility. It also includes configuration to use Redis for caching, although that must be enabled post-install in .platform.app.yaml.

GovCMS is a Drupal distribution built for the Australian government, and includes configuration optimized for managing government websites.

Services:

  • PHP 7.2
  • MariaDB 10.2
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Laravel

Laravel

This template provides a basic Laravel skeleton. It comes pre-configured to use a MariaDB database and Redis for caching and sessions.

Laravel is an opinionated, integrated rapid-application-development framework for PHP.

Services:

  • PHP 7.3
  • MariaDB 10.2
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Magento 2 Community Edition

Magento 2 Community Edition

This template builds Magento 2 CE on Platform.sh. It includes additional scripts to customize Magento to run effectively in a build-and-deploy environment.

Magento is a fully integrated ecommerce system and web store written in PHP. This is the Open Source version.

Services:

  • PHP 7.2
  • MariaDB 10.2
  • Redis 3.2

View the repository on GitHub.

Deploy on Platform.sh

Mautic

Mautic

TThis template provides a basic Mautic installation.

Mautic is an Open SOurce marketing automation tool built on Symfony.

Services:

  • PHP 7.2
  • MariaDB 10.4
  • RabbitMQ 3.7

View the repository on GitHub.

Deploy on Platform.sh

Nextcloud

Nextcloud

This template builds Nextcloud on Platform.sh.

Nextcloud is a PHP-based groupware server with installable apps, file synchronization, and federated storage.

An adminstrative user will be created automatically. See the deploy log after the project is installed for the name and password.

Services:

  • PHP 7.4
  • MariaDB 10.4
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Opigno

Opigno

This template builds the Opigno Drupal 8 distribution using the Drupal Composer project for better flexibility. It also includes configuration to use Redis for caching, although that must be enabled post-install in .platform.app.yaml.

Opigno is a Learning Management system built as a Drupal distribution.

Services:

  • PHP 7.3
  • MariaDB 10.4
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Pimcore

Pimcore

Pimcore Digital Platform for Enterprises

Services:

  • PHP 7.4
  • MariaDB 10.2
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Sculpin

Sculpin

This template provides a basic Sculpin skeleton. All files are generated at build time, so at runtime only static files need to be served.

Sculpin is a static site generator written in PHP and using the Twig templating engine.

Services:

  • PHP 7.3

View the repository on GitHub.

Deploy on Platform.sh

Symfony 3

Symfony 3

This template provides a basic Symfony 3 skeleton. It is configured for Production mode by default so the usual Symfony "welcome" page will not appear.

Symfony is a high-performance loosely-coupled PHP web development framework. Version 3 is the legacy support version.

Services:

  • PHP 7.2
  • MariaDB 10.2

View the repository on GitHub.

Deploy on Platform.sh

Symfony 4

Symfony 4

This template provides a basic Symfony 4 skeleton. It is configured for Production mode by default so the usual Symfony "welcome" page will not appear. That can be adjusted in .platform.app.yaml.

Symfony is a high-performance loosely-coupled PHP web development framework.

Services:

  • PHP 7.3
  • MariaDB 10.4

View the repository on GitHub.

Deploy on Platform.sh

Symfony 5

Symfony 5

This template provides a basic Symfony 5 skeleton. It is configured for Production mode by default so the usual Symfony "welcome" page will not appear. That can be adjusted in .platform.app.yaml.

Symfony is a high-performance loosely-coupled PHP web development framework.

Services:

  • PHP 7.3
  • MariaDB 10.4

View the repository on GitHub.

Deploy on Platform.sh

TYPO3

TYPO3

This template provides a basic TYPO3 installation.

TYPO3 is a PHP-based Content Management System

Services:

  • PHP 7.4
  • MariaDB 10.4
  • Redis 5.0

View the repository on GitHub.

Deploy on Platform.sh

Wordpress

Wordpress

This template builds WordPress on Platform.sh using the johnbolch/wordpress "Composer Fork" of WordPress. Plugins and themes should be managed with Composer exclusively.

WordPress is a blogging and lightweight CMS written in PHP.

Services:

  • PHP 7.3
  • MariaDB 10.2

View the repository on GitHub.

Deploy on Platform.sh