The Next Generation Platform is Coming - Get Ready Now!

Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up

Getting Started on Heroku Fir with PHP

Introduction

Complete this tutorial to deploy a sample PHP app to Heroku Private Spaces on the Fir generation of the platform. To deploy the app to the Common Runtime or Cedar Private Spaces, follow this guide instead.

The tutorial assumes that you have:

  • A verified Heroku Account;
  • An existing Fir Private Space;
  • A team admin or member role that has the app creation permission on the space;
  • An SSH key added to your Heroku account;
  • PHP version 8 installed locally - see the Installation and Configuration section of the PHP Manual;
  • Composer installed locally - see the Getting Started section of the Composer documentation.

Using dynos and databases to complete this tutorial counts towards your usage. We recommend using 1X-Classic dynos and an Essential-0 Postgres database to complete this tutorial. Delete all resources after completing the tutorial.

Set Up

Install the Heroku Command Line Interface (CLI). Use the CLI to manage and scale your app, provision add-ons, view your logs, and run your app locally.

The Heroku CLI requires Git, the popular version control system. If you don’t already have Git installed, complete the following before proceeding:

  • Git installation
  • First-time Git setup

Download and run the installer for your platform:

apple logomacOS

Install Homebrew and run:

$ brew tap heroku/brew && brew install heroku

windows logoWindows

Download the appropriate installer for your Windows installation:

64-bit installer

32-bit installer

You can find more installation options for the Heroku CLI here.

After installation, you can use the heroku command from your command shell.

On Windows, start the Command Prompt (cmd.exe) or Powershell to access the command shell.

To log in to the Heroku CLI, use the heroku login command:

$ heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/cli/browser/***
heroku: Waiting for login...
Logging in... done
Logged in as [email protected]

This command opens your web browser to the Heroku login page. If your browser is already logged in to Heroku, click the Log In button on the page.

This authentication is required for the heroku and git commands to work correctly.

If you have any problems installing or using the Heroku CLI, see the main Heroku CLI article for advice and troubleshooting steps.

If you’re behind a firewall that uses a proxy to connect with external HTTP/HTTPS services, set the HTTP_PROXY or HTTPS_PROXY environment variables in your local development environment before running the heroku command.

Clone the Sample App

If you’re new to Heroku, it’s recommended that you complete this tutorial using the Heroku-provided sample application.

To deploy an existing application, follow the Preparing a Codebase for Heroku Deployment article instead.

Clone the sample application to get a local version of the code. Execute these commands in your local command shell or terminal:

$ git clone https://github.com/heroku/php-getting-started.git
$ cd php-getting-started

You now have a functioning Git repository with a simple PHP app that uses the Slim micro framework. It includes a composer.json file that specifies the necessary dependencies, as well as a version range for the PHP language runtime.

Define a Procfile

Use a Procfile, a text file in the root directory of your application, to explicitly declare what command to execute to start your app.

The Procfile in the example app looks like this:

web: heroku-php-apache2 web/

This Procfile declares a single process type, web, and the command needed to run it. The name web is important here. It declares that this process type is attached to Heroku’s HTTP routing stack and receives web traffic when deployed. The command used here starts PHP’s FPM server together with Apache HTTPD, the web server, and instructs it to serve the app from the web/ directory of the codebase.

A Procfile can contain additional process types. For example, you can declare a background worker process that processes items off a queue.

IPv6 Compatibility

Fir uses IPv6 to route web requests. When using Apache HTTPD or Nginx for an app on Heroku (using the heroku-php-apache2 or heroku-php-nginx commands in Procfile), they will automatically bind to the correct interface and port on startup.

Sometimes, you may wish to use PHP’s built-in web server during early development of your app. In that case, it is important to have the server listen on IPv6 interfaces in order to allow traffic to reach the dyno.

Instead of using the IPv4 address “0.0.0.0” to listen on all IPv4 interfaces, the IPv6 shorthand address “::” is used. Because colons (“:”) are also used to separate addresses and ports, IPv6 addresses are wrapped in square brackets, for example “[::]”. Many command shells treat square brackets as special characters, so such an address must be wrapped in quotation marks. Since the port number a Heroku app must bind to is available in the PORT environment variable, double quotation marks are used to allow the variable to expand.

Putting all of these instructions together, a possible Procfile that serves our getting started app using PHP’s built-in web server from the web/ subdirectory of the app could look like this:

web: php -S "[::]:${PORT}" -t "web/"

In most cases, however, you will not need to use this approach, as Apache HTTPD or Nginx are the recommended web servers for production applications.

Create Your App in a Fir Space

Delete your app and database as soon as you’re done to control costs.

 

You can get a list of all Heroku spaces by running $ heroku spaces

Create an app on Heroku to prepare the platform to receive your source code by replacing <space-name> with the name of your Fir space in the command below:

$ heroku create --space <space-name>
Creating app in space <space name>...
Creating app in space <space name>... done, infinite-springs-62063
http://infinite-springs-62063-0ba126a743db.herokuapp.com/ | https://git.heroku.com/infinite-springs-62063.git

When you create an app, a Git remote called heroku also gets created and associated with your local Git repository. Git remotes are versions of your repository that live on other servers. You deploy your app by pushing its code to that special Heroku-hosted remote associated with your app.

Heroku generates a random name for your app, in this case, infinite-springs-62063. You can specify your own app name.

Deploy the App

Using a dyno to complete this tutorial counts towards your usage. Delete your app and database as soon as you’re done to control costs.

Deploy your code. This command pushes the main branch of the sample repo to your heroku remote, which then deploys to Heroku:

$ git push heroku main
remote: Updated 17 paths from a3f5634
remote: Compressing source files... done.
remote: Building source:
remote: Extracting source
remote: Image with name "infinite-springs-62063/builds" not found
remote: 2 of 3 buildpacks participating
remote: heroku/php      0.2.0
remote: heroku/procfile 3.1.2
remote:
remote: [Bootstrapping]
remote:
remote: [Preparing platform packages installation]
remote:
remote: [Installing platform packages]
remote: No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
remote: Loading composer repositories with package information
remote: Updating dependencies
remote: Lock file operations: 26 installs, 0 updates, 0 removals
remote:   - Locking fig/http-message-util (1.1.5)
remote:   - Locking heroku-sys/composer (2.8.2)
remote:   - Locking heroku-sys/php (8.3.13)
remote:   - Locking heroku/installer-plugin (1.8.0)
remote:   - Locking laravel/serializable-closure (v1.3.7)
remote:   - Locking monolog/monolog (3.8.0)
remote:   - Locking nikic/fast-route (v1.3.0)
remote:   - Locking php-di/invoker (2.3.4)
remote:   - Locking php-di/php-di (7.0.7)
remote:   - Locking php-di/slim-bridge (3.4.1)
remote:   - Locking psr/container (2.0.2)
remote:   - Locking psr/http-factory (1.1.0)
remote:   - Locking psr/http-message (2.0)
remote:   - Locking psr/http-server-handler (1.0.2)
remote:   - Locking psr/http-server-middleware (1.0.2)
remote:   - Locking psr/log (3.0.2)
remote:   - Locking ralouphie/getallheaders (3.0.3)
remote:   - Locking slim/psr7 (1.7.0)
remote:   - Locking slim/slim (4.14.0)
remote:   - Locking slim/twig-view (3.4.1)
remote:   - Locking symfony/deprecation-contracts (v3.5.1)
remote:   - Locking symfony/polyfill-ctype (v1.31.0)
remote:   - Locking symfony/polyfill-mbstring (v1.31.0)
remote:   - Locking symfony/polyfill-php80 (v1.31.0)
remote:   - Locking symfony/polyfill-php81 (v1.31.0)
remote:   - Locking twig/twig (v3.16.0)
remote: Writing lock file
remote: Installing dependencies from lock file
remote: Package operations: 26 installs, 0 updates, 0 removals
remote:   - Installing heroku/installer-plugin (1.8.0): Mirroring from /layers/heroku_php/bootstrap_installer/support/installer
remote:   - Downloading heroku-sys/php (8.3.13)
remote:   - Downloading heroku-sys/composer (2.8.2)
remote:  0/2 [>---------------------------]   0%
remote:  1/2 [==============>-------------]  50%
remote:  2/2 [============================] 100%
remote:   - Installing heroku-sys/php (8.3.13)
remote:   - Installing fig/http-message-util (1.1.5)
remote:   - Installing heroku-sys/composer (2.8.2)
remote:   - Installing laravel/serializable-closure (v1.3.7)
remote:   - Installing monolog/monolog (3.8.0)
remote:   - Installing nikic/fast-route (v1.3.0)
remote:   - Installing php-di/invoker (2.3.4)
remote:   - Installing php-di/php-di (7.0.7)
remote:   - Installing php-di/slim-bridge (3.4.1)
remote:   - Installing psr/container (2.0.2)
remote:   - Installing psr/http-factory (1.1.0)
remote:   - Installing psr/http-message (2.0)
remote:   - Installing psr/http-server-handler (1.0.2)
remote:   - Installing psr/http-server-middleware (1.0.2)
remote:   - Installing psr/log (3.0.2)
remote:   - Installing ralouphie/getallheaders (3.0.3)
remote:   - Installing slim/psr7 (1.7.0)
remote:   - Installing slim/slim (4.14.0)
remote:   - Installing slim/twig-view (3.4.1)
remote:   - Installing symfony/deprecation-contracts (v3.5.1)
remote:   - Installing symfony/polyfill-ctype (v1.31.0)
remote:   - Installing symfony/polyfill-mbstring (v1.31.0)
remote:   - Installing symfony/polyfill-php80 (v1.31.0)
remote:   - Installing symfony/polyfill-php81 (v1.31.0)
remote:   - Installing twig/twig (v3.16.0)
remote:     0 [>---------------------------]    0 [->--------------------------]
remote: Generating autoload files
remote:
remote: [Installing web servers]
remote: No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
remote: Loading composer repositories with package information
remote: Updating dependencies
remote: Lock file operations: 4 installs, 0 updates, 0 removals
remote:   - Locking heroku-sys/apache (2.4.62)
remote:   - Locking heroku-sys/nginx (1.26.2)
remote:   - Locking heroku/heroku-buildpack-php (dev-bundled)
remote:   - Locking heroku/installer-plugin (1.8.0)
remote: Writing lock file
remote: Installing dependencies from lock file
remote: Package operations: 4 installs, 0 updates, 0 removals
remote:   - Installing heroku/installer-plugin (1.8.0): Mirroring from /layers/heroku_php/bootstrap_installer/support/installer
remote:   - Downloading heroku-sys/apache (2.4.62)
remote:   - Downloading heroku-sys/nginx (1.26.2)
remote:  0/2 [>---------------------------]   0%
remote:  2/2 [============================] 100%
remote:   - Installing heroku-sys/apache (2.4.62)
remote:   - Installing heroku-sys/nginx (1.26.2)
remote:   - Installing heroku/heroku-buildpack-php (dev-bundled): Mirroring from /layers/heroku_php/bootstrap_installer
remote:     0 [>---------------------------]    0 [->--------------------------]
remote: Generating autoload files
remote:
remote: [Installing dependencies]
remote: Installing dependencies from lock file
remote: Verifying lock file contents can be installed on current platform.
remote: Dependency resolution completed in 0.000 seconds
remote: Package operations: 23 installs, 0 updates, 0 removals
remote: Installs: laravel/serializable-closure:v1.3.7, psr/log:3.0.2, monolog/monolog:3.8.0, nikic/fast-route:v1.3.0, psr/http-message:2.0, psr/http-server-handler:1.0.2, psr/http-server-middleware:1.0.2, psr/http-factory:1.1.0, psr/container:2.0.2, slim/slim:4.14.0, php-di/invoker:2.3.4, php-di/php-di:7.0.7, php-di/slim-bridge:3.4.1, symfony/polyfill-php80:v1.31.0, ralouphie/getallheaders:3.0.3, fig/http-message-util:1.1.5, slim/psr7:1.7.0, symfony/polyfill-php81:v1.31.0, symfony/polyfill-mbstring:v1.31.0, symfony/polyfill-ctype:v1.31.0, symfony/deprecation-contracts:v3.5.1, twig/twig:v3.16.0, slim/twig-view:3.4.1
remote:   - Downloading laravel/serializable-closure (v1.3.7)
remote:   - Downloading psr/log (3.0.2)
remote:   - Downloading monolog/monolog (3.8.0)
remote:   - Downloading nikic/fast-route (v1.3.0)
remote:   - Downloading psr/http-message (2.0)
remote:   - Downloading psr/http-server-handler (1.0.2)
remote:   - Downloading psr/http-server-middleware (1.0.2)
remote:   - Downloading psr/http-factory (1.1.0)
remote:   - Downloading psr/container (2.0.2)
remote:   - Downloading slim/slim (4.14.0)
remote:   - Downloading php-di/invoker (2.3.4)
remote:   - Downloading php-di/php-di (7.0.7)
remote:   - Downloading php-di/slim-bridge (3.4.1)
remote:   - Downloading symfony/polyfill-php80 (v1.31.0)
remote:   - Downloading ralouphie/getallheaders (3.0.3)
remote:   - Downloading fig/http-message-util (1.1.5)
remote:   - Downloading slim/psr7 (1.7.0)
remote:   - Downloading symfony/polyfill-php81 (v1.31.0)
remote:   - Downloading symfony/polyfill-mbstring (v1.31.0)
remote:   - Downloading symfony/polyfill-ctype (v1.31.0)
remote:   - Downloading symfony/deprecation-contracts (v3.5.1)
remote:   - Downloading twig/twig (v3.16.0)
remote:   - Downloading slim/twig-view (3.4.1)
remote:   - Installing laravel/serializable-closure (v1.3.7): Extracting archive
remote:   - Installing psr/log (3.0.2): Extracting archive
remote:   - Installing monolog/monolog (3.8.0): Extracting archive
remote:   - Installing nikic/fast-route (v1.3.0): Extracting archive
remote:   - Installing psr/http-message (2.0): Extracting archive
remote:   - Installing psr/http-server-handler (1.0.2): Extracting archive
remote:   - Installing psr/http-server-middleware (1.0.2): Extracting archive
remote:   - Installing psr/http-factory (1.1.0): Extracting archive
remote:   - Installing psr/container (2.0.2): Extracting archive
remote:   - Installing slim/slim (4.14.0): Extracting archive
remote:   - Installing php-di/invoker (2.3.4): Extracting archive
remote:   - Installing php-di/php-di (7.0.7): Extracting archive
remote:   - Installing php-di/slim-bridge (3.4.1): Extracting archive
remote:   - Installing symfony/polyfill-php80 (v1.31.0): Extracting archive
remote:   - Installing ralouphie/getallheaders (3.0.3): Extracting archive
remote:   - Installing fig/http-message-util (1.1.5): Extracting archive
remote:   - Installing slim/psr7 (1.7.0): Extracting archive
remote:   - Installing symfony/polyfill-php81 (v1.31.0): Extracting archive
remote:   - Installing symfony/polyfill-mbstring (v1.31.0): Extracting archive
remote:   - Installing symfony/polyfill-ctype (v1.31.0): Extracting archive
remote:   - Installing symfony/deprecation-contracts (v3.5.1): Extracting archive
remote:   - Installing twig/twig (v3.16.0): Extracting archive
remote:   - Installing slim/twig-view (3.4.1): Extracting archive
remote: Generating optimized autoload files
remote: 10 packages you are using are looking for funding.
remote: Use the `composer fund` command to find out more!
remote:
remote: [Preparing Composer runtime environment]
remote:
remote: [Discovering process types]
remote: Procfile declares types -> web
remote: Adding layer 'heroku/php:composer_env'
remote: Adding layer 'heroku/php:platform'
remote: Adding layer 'heroku/php:webservers'
remote: Adding layer 'buildpacksio/lifecycle:launch.sbom'
remote: Added 1/1 app layer(s)
remote: Adding layer 'buildpacksio/lifecycle:launcher'
remote: Adding layer 'buildpacksio/lifecycle:config'
remote: Adding layer 'buildpacksio/lifecycle:process-types'
remote: Adding label 'io.buildpacks.lifecycle.metadata'
remote: Adding label 'io.buildpacks.build.metadata'
remote: Adding label 'io.buildpacks.project.metadata'
remote: Setting default process type 'web'
remote: Saving infinite-springs-62063/builds...
remote: *** Images (sha256:80061b72beca63ddc7ec94d764569c2a3924c7f357e1d70a8d996c01e682ecd3):
remote:       infinite-springs-62063/builds:f6de4260-88d4-49b4-9c38-3d078f102164
remote: Adding cache layer 'heroku/php:composer_cache'
remote: Adding cache layer 'heroku/php:platform_cache'
remote: Uploading cache
remote: Launching...
remote: http://infinite-springs-62063-0ba126a743db.herokuapp.com/ deployed to Heroku
remote: Verifying deploy... done.
To https://git.heroku.com/infinite-springs-62063.git
 * [new branch]      main -> main

The app is now deployed. The default dyno size for Fir Private Spaces is 1X-Classic.

Visit the app at the URL shown in the logs. As a shortcut, you can also open the website as follows:

$ heroku open

View Logs

Fir apps do not retain log history like Cedar apps. To view an event in your Fir logs, you must run the logging command while that event occurs.

Heroku treats logs as streams of time-ordered events, aggregated from the output streams of all your app and Heroku components. Heroku provides a single stream for all events. View information about your running app by using one of the logging commands:

$ heroku logs
Fetching logs...

2024-12-16T20:17:12.072129+00:00 app[web-77f9f8f475-tx4mx]: [2024-12-16T20:17:11.891691+00:00] default.DEBUG: logging output. [] []
2024-12-16T20:17:12.072188+00:00 app[web-77f9f8f475-tx4mx]: 2600:1f18:5cec:4a05:40b::18 - - [16/Dec/2024:20:17:11 +0000] "GET / HTTP/1.1" 200 8798 "-" "curl/8.7.1
2024-12-16T20:17:15.558524+00:00 heroku-router[web]: at=info method=GET path="/" host=infinite-springs-62063-0ba126a743db.herokuapp.com request_id=190dbba4-08d2-79ae-7318-1221c96a6cbd fwd="13.110.54.15" dyno=web-77f9f8f475-tx4mx connect=1ms service=7ms status=200 bytes=8798 protocol=http tls_version=tls1.3

To generate more log messages, refresh the app in your browser.

To stop streaming the logs, press Ctrl+C.

Install App Dependencies Locally

Heroku recognizes an app as a PHP app by the existence of a composer.json file in the root directory.

The demo app you deployed already has a composer.json:

{
  "require" : {
    "php": "^8.1",
    "php-di/php-di": "^7.0",
    "slim/slim": "^4.0",
    "php-di/slim-bridge": "^3.4.1",
    "slim/psr7": "^1.3.0",
...

The composer.json file specifies the dependencies to install with your application. It also determines the version of PHP used to run your application on Heroku, and which PHP extensions to enable.

Every app that has dependencies listed in composer.json also requires the corresponding composer.lock lock file to be present. Any time composer.json is modified, composer update must be run to ensure the lock file is up to date. Running composer install installs the dependencies that were “frozen” to composer.lock during the last update.

When an app deploys, Heroku installs the appropriate PHP and extension packages, as well as regular dependencies, listed in the lock file.

Run composer install in your local directory to install the dependencies, preparing your system for running the app locally:

$ composer install
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Package operations: 24 installs, 0 updates, 0 removals
    0 [>---------------------------]    0 [->--------------------------]
  - Installing heroku/heroku-buildpack-php (v259): Extracting archive
  - Installing laravel/serializable-closure (v1.3.7): Extracting archive
  - Installing psr/log (3.0.2): Extracting archive
  - Installing monolog/monolog (3.8.0): Extracting archive

After installing dependencies, you can run your app locally.

Push Local Changes

In this step, you propagate a local change to the application to Heroku.

First, we modify composer.json to include an additional dependency for the alkri11es/cowsayphp library:

$ composer require alrik11es/cowsayphp
./composer.json has been updated
Running composer update alrik11es/cowsayphp
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking alrik11es/cowsayphp (1.2.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing alrik11es/cowsayphp (1.2.0): Extracting archive
Generating autoload files
10 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
No security vulnerability advisories found.
Using version ^1.2 for alrik11es/cowsayphp

This will modify the “require” section of composer.json for you, and update the composer.lock lock file to reflect the newly installed dependency.

If you introduced the dependency by changing the composer.json file yourself, be sure to update the dependencies by running composer update instead.

The library is now ready for use. Since our example app uses PHP-DI for Dependency Injection, you must first add another entry to the container near the top of the file, so that PHP-DI knows how to instantiate an animal class instance from the Cowsayphp\Farm namespace when a Cowsayphp\AnimalInterface is used.

In file web/index.php, on line 28 add:

// Add Cowsay to Container
$container->set(\Cowsayphp\AnimalInterface::class, function() {
  return \Cowsayphp\Farm::create(\Cowsayphp\Farm\Cow::class);
});

When definining a new route near the bottom of web/index.php, its function’s AnimalInterface type hint will cause PHP-DI to automatically inject a Cow farm animal instance.

In file web/index.php, on line 42 add:

$app->get('/coolbeans', function(Request $request, Response $response, LoggerInterface $logger, \Cowsayphp\AnimalInterface $animal) {
  $logger->debug('letting the Cowsay library write something cool.');
  $response->getBody()->write("<pre>".$animal->say("Cool beans")."</pre>");
  return $response;
});

Now deploy this local change to Heroku.

Almost every deploy to Heroku follows this same pattern. First, add the modified files to the local Git repository:

$ git add composer.json composer.lock web/index.php

Then commit the changes to the repository:

$ git commit -m "Added cowsay library and endpoint"
[main d6689d3] Added cowsay library and endpoint
 3 files changed, 70 insertions(+), 2 deletions(-)

Next, deploy just as before:

$ git push heroku main

Finally, check that everything is working by navigating to the new /coolbeans endpoint of the app:

$ heroku open coolbeans

Debugging

The Heroku PHP Cloud Native Buildpack (CNB) turns your code into an Open Container Initiative (OCI) container image when you deploy to Fir. This image gets executed on our dynos.

You can use this image locally to reproduce and debug deployment problems. Build an OCI image from your application to debug locally by using the Heroku PHP CNB. If you’re interested, check out the PHP CNB tutorial.

Start a Console

The heroku run command to launch an interactive one-off dyno is unavailable for Fir. As an alternative, use heroku run:inside to access a running dyno until we add heroku run for Fir.

You must add an SSH key to your Heroku account before running this command.

To execute the command you need the name of a currently running process. You can see a list with heroku ps:

$ heroku ps
=== web (1X-Classic): heroku-php-apache2 web/ (1)

web-77f9f8f475-tx4mx: up 2024/12/16 14:17:11 -0600 (~ 8s ago)

Use that dyno name to run a command like php -v:

$ heroku run:inside web-77f9f8f475-tx4mx "php -v"
Running launcher php -v on infinite-springs-62063...
Running launcher php -v on infinite-springs-62063... up, web-77f9f8f475-tx4mx
PHP 8.3.13 (cli) (built: Nov  1 2024 11:24:33) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.13, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.13, Copyright (c), by Zend Technologies

If you receive an error, Error connecting to process, configure your firewall.

Let’s try another example. Create another one-off process within your running dyno and run the bash command to open up a shell on that dyno. You can use this to view the contents of the dyno’s disk:

$ heroku run:inside web-77f9f8f475-tx4mx "bash"
Running launcher bash on infinite-springs-62063...
Running launcher bash on infinite-springs-62063... up, web-77f9f8f475-tx4mx
heroku@web-77f9f8f475-tx4mx:/workspace$ ls -lah
total 80K
drwxrwsrwx.  6 heroku heroku  168 Jan  1  1980 .
drwxr-xr-x.  1 root   root     54 Dec 16 20:17 ..
drwxr-xr-x.  2 heroku heroku   46 Jan  1  1980 .github
-rw-r--r--.  1 heroku heroku   13 Jan  1  1980 .gitignore
-rw-r--r--.  1 heroku heroku   29 Jan  1  1980 Procfile
-rw-r--r--.  1 heroku heroku 2.5K Jan  1  1980 README.md
-rw-r--r--.  1 heroku heroku  199 Jan  1  1980 app.json
-rw-r--r--.  1 heroku heroku  308 Jan  1  1980 composer.json
-rw-r--r--.  1 heroku heroku  57K Jan  1  1980 composer.lock
drwxr-sr-x. 13 heroku heroku  177 Jan  1  1980 vendor
drwxr-xr-x.  2 heroku heroku   78 Jan  1  1980 views
drwxr-xr-x.  4 heroku heroku   73 Jan  1  1980 web
heroku@web-77f9f8f475-tx4mx:/workspace$ exit
exit

Type exit to exit the shell.

Define Config Vars

Heroku lets you externalize configuration by storing data such as encryption keys or external resource addresses in config vars.

At runtime, we expose config vars as environment variables to the application, which you can read using PHP’s getenv function. This way, your app source does not contain hard-coded credentials for databases, external API endpoints and so forth.

In addition to storing credentials such as database connection information, it’s also common practice to place configuration flags or values that control essential behavior of your app into config vars.

For example, the Cowsay library supports other types of animals, and this example will use an environment variable named COWSAY_FARM_CLASS to control which class is used. In web/index.php, locate the section you added earlier, and modify it so that the DI container uses the value of the environment variable to construct the class name dynamically:

// Add Cowsay to Container
$container->set(\Cowsayphp\AnimalInterface::class, function() {
  $class = '\\Cowsayphp\\Farm\\'.(getenv("COWSAY_FARM_CLASS")?:'Cow');
  return \Cowsayphp\Farm::create($class);
});

If the environment variable is not set, it will default to returning a Cow instance, but if you now set the COWSAY_FARM_CLASS config var on your app to another value, it will be used instead:

$ heroku config:set COWSAY_FARM_CLASS=Dragon
Setting COWSAY_FARM_CLASS and restarting infinite-springs-62063...
Setting COWSAY_FARM_CLASS and restarting infinite-springs-62063... done, v4
COWSAY_FARM_CLASS: Dragon

To see this change in action, deploy your changed application to Heroku once again, using the same git add web/index.php, git commit, and git push sequence of commands you used earlier.

Delete Your App

Remove the app from your account. We only charge you for the resources you used.

This action permanently deletes your application and any add-ons attached to it.

$ heroku apps:destroy

You can confirm that your app is gone with this command:

$ heroku apps --all

Next Steps

You now know how to configure and deploy a PHP app, view logs, and start a console.

To learn more, see:

Here’s some recommended reading:

  • How Heroku Works is a technical overview of the concepts you encounter while writing, configuring, deploying, and running applications.
  • Preparing a Codebase for Heroku Deployment contains general deployment recommendations, while Deploying PHP Apps on Heroku offers more PHP-specific guidance.
  • The PHP support category contains many resources for managing and deploying PHP apps on Heroku.
  • Framework-specific articles contain useful pointers for deploying Laravel and Symfony apps.

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • heroku.com
  • Terms of Service
  • Privacy (日本語)
  • Cookies
  • Cookie Preferences
  • Your Privacy Choices
  • © 2025 Salesforce.com