{
    "href": "/post/2026/02/08/designing-a-bootstrap-script/",
    "relId": "2026/02/08/designing-a-bootstrap-script",
    "title": "Designing a Bootstrap Script",
    "author": "pmjones",
    "tags": [
        {
            "href": "/tag/programming/",
            "relId": "programming",
            "title": "Programming",
            "author": null,
            "created": null,
            "updated": [],
            "markup": "markdown"
        },
        {
            "href": "/tag/php/",
            "relId": "php",
            "title": "PHP",
            "author": null,
            "created": null,
            "updated": [],
            "markup": "markdown"
        },
        {
            "href": "/tag/interop/",
            "relId": "interop",
            "title": "Interop",
            "author": null,
            "created": "2025-02-03 20:11:59 UTC",
            "updated": [
                "2025-02-03 20:11:59 UTC"
            ],
            "markup": "markdown"
        }
    ],
    "created": "2026-02-08 18:37:30 UTC",
    "updated": [
        "2026-02-08 18:37:30 UTC",
        "2026-02-08 18:39:22 UTC",
        "2026-02-08 18:42:02 UTC",
        "2026-02-08 18:42:42 UTC",
        "2026-02-08 18:44:00 UTC",
        "2026-02-08 18:44:22 UTC",
        "2026-02-08 18:46:17 UTC",
        "2026-02-09 13:39:24 UTC",
        "2026-02-09 13:45:34 UTC"
    ],
    "markup": "markdown",
    "html": "<p>bluf:</p>\n<pre><code class=\"language-php\">require dirname(__DIR__) . '/vendor/autoload.php';\n\nuse Project\\Bootstrap\\ContainerFactory;\nuse Project\\Presentation\\Http\\HttpFrontController;\n\nnew ContainerFactory()\n    -&gt;newContainer()\n    -&gt;getService(HttpFrontController::class)\n    -&gt;run();\n</code></pre>\n<hr>\n<h3>I.</h3>\n<p>Most PHP systems these days have a bootstrap script (<code>index.php</code> for the web, or <code>console.php</code> for the command line). I've had to review a lot of these as part of my <a href=\"https://leanpub.com/mlaphp\">legacy modernization</a> work.</p>\n<p>The following survey of public PHP frameworks should give you a good idea of the wide range of design choices made by different bootstrap authors:</p>\n<ul>\n<li>Aura        : <a href=\"https://github.com/auraphp/Aura.Web_Project/blob/master/web/index.php\"><code>web/index.php</code></a>\n</li>\n<li>Bear        : <a href=\"https://github.com/bearsunday/BEAR.Sunday/blob/1.x/demo/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>CakePHP     : <a href=\"https://github.com/cakephp/app/blob/5.x/webroot/index.php\"><code>webroot/index.php</code></a>\n</li>\n<li>CodeIgniter : <a href=\"https://github.com/bcit-ci/CodeIgniter/blob/develop/index.php\"><code>index.php</code></a>\n</li>\n<li>FatFree     : <a href=\"https://github.com/bcosca/fatfree/blob/master/index.php\"><code>index.php</code></a>\n</li>\n<li>FlightPHP   : <code>public/index.php</code> points to <a href=\"https://github.com/flightphp/skeleton/blob/master/app/config/bootstrap.php\"><code>app/config/bootstrap.php</code></a>\n</li>\n<li>FuelPHP     : <a href=\"https://github.com/fuel/fuel/blob/1.8/master/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>Joomla      : <code>index.php</code> points to <a href=\"https://github.com/joomla/joomla-cms/blob/5.4-dev/includes/app.php\"><code>includes/app.php</code></a>\n</li>\n<li>Klein       : <a href=\"https://github.com/klein/klein.php?tab=readme-ov-file#example\">(documentation)</a>\n</li>\n<li>Kohana      : <a href=\"https://github.com/kohana/kohana/blob/3.3/master/index.php\"><code>index.php</code></a>\n</li>\n<li>Laminas-MVC : <a href=\"https://github.com/laminas/laminas-mvc-skeleton/blob/2.5.x/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>Laravel     : <a href=\"https://github.com/laravel/laravel/blob/12.x/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>LeafPHP     : <a href=\"https://github.com/leafsphp/leafMVC/blob/v4.x/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>LightMVC    : <a href=\"https://github.com/lightmvc/lightmvcskel/blob/master/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>Lithium     : <a href=\"https://github.com/UnionOfRAD/framework/blob/1.2/webroot/index.php\"><code>webroot/index.php</code></a>\n</li>\n<li>Mezzio      : <a href=\"https://github.com/mezzio/mezzio-skeleton/blob/3.18.x/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>Nette       : <a href=\"https://github.com/nette-examples/quickstart/blob/v4.0/www/index.php\"><code>www/index.php</code></a>\n</li>\n<li>Phalcon     : <a href=\"https://github.com/phalcon/tutorial/blob/master/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>PHPixie     : <a href=\"https://github.com/dracony/PHPixie-Sample-App/blob/master/web/index.php\"><code>web/index.php</code></a>\n</li>\n<li>Silex       : <a href=\"https://github.com/silexphp/Silex?tab=readme-ov-file#silex-a-simple-web-framework\">(documentation)</a>\n</li>\n<li>Slim        : <a href=\"https://www.slimframework.com/docs/v4/objects/application.html\">(documentation)</a>\n</li>\n<li>Symfony     : <code>public/index.php</code> points to a <a href=\"https://github.com/symfony/runtime/blob/8.1/Internal/autoload_runtime.template\">runtime template</a>\n</li>\n<li>Tempest     : <a href=\"https://github.com/tempestphp/tempest-framework/blob/3.x/public/index.php\"><code>public/index.php</code></a>\n</li>\n<li>Yii         : <a href=\"https://github.com/yiisoft/demo/blob/master/blog/public/index.php\"><code>public/index.php</code></a>\n</li>\n</ul>\n<p>Each of the bootstrap scripts performs at least a few of the following tasks, though not all of them perform all of the tasks, and none of them necessarily in this order:</p>\n<ul>\n<li>Require an autoloader</li>\n<li>Call <code>ini_set()</code>, <code>error_reporting()</code>, etc.</li>\n<li>Start/stop a timer to measure execution time</li>\n<li>Load or set environment variables</li>\n<li>Check the SAPI or the environment to change how the bootstrap behaves</li>\n<li>Explicitly <code>require</code> framework-specific library files</li>\n<li>Determine directory paths (e.g. the project path, config path, app path, document root, etc.)</li>\n<li>Load or set framework-specific configuration files and values</li>\n<li>Load or set middleware stacks</li>\n<li>Load or set routes</li>\n<li>Instantiate a request object</li>\n<li>Create a service container</li>\n<li>Add or register services on a container</li>\n<li>Instantiate or obtain a front controller</li>\n<li>Dispatch the request through a router</li>\n<li>Invoke the front controller</li>\n<li>Send a response object</li>\n<li>Call <code>exit()</code>\n</li>\n</ul>\n<p>Often enough, one or more of the above tasks is accomplished by including another script from the bootstrap. The <a href=\"https://github.com/bcit-ci/CodeIgniter/blob/develop/index.php\">CodeIgniter</a> and <a href=\"https://github.com/joomla/joomla-cms/blob/5.4-dev/index.php\">Joomla</a> bootstrap scripts are typical examples of that. They are a lot like what I see from many in-house PHP bootstrap scripts.</p>\n<h3>II.</h3>\n<p>Looking again at that list of tasks above, is there a way to consolidate some\nof them into more appropriate locations and maybe even describe a common\napproach to bootstrap scripts?</p>\n<p>First, they could use autoloading exclusively, instead of using <code>require</code> to bring\nin other scripts. That gets rid of:</p>\n<ul>\n<li>Explicitly require framework-specific library files</li>\n</ul>\n<p>Next, given the existence of a service container, all configuration tasks could\nbe encapsulated by configuration objects in (or service providers to) that\ncontainer. Doing so would consolidate these tasks, moving the relevant logic out of\nscripts and into more-easily-tested units:</p>\n<ul>\n<li>Call <code>ini_set()</code>, <code>error_reporting()</code>, etc.</li>\n<li>Start/stop a timer to measure execution time</li>\n<li>Add or register services on a container</li>\n<li>Determine directory paths (e.g. the project path, config path, app path, document root, etc.)</li>\n<li>Load or set framework-specific configuration files and values</li>\n<li>Load or set middleware stacks</li>\n<li>Load or set routes</li>\n</ul>\n<p>After that, request and response work can be moved into the front controller\nitself, removing these tasks from the bootstrap:</p>\n<ul>\n<li>Instantiate a request object</li>\n<li>Dispatch the request through a router</li>\n<li>Send a response object</li>\n</ul>\n<p>That leaves only these remaining tasks:</p>\n<ul>\n<li>Require an autoloader</li>\n<li>Load or set environment variables</li>\n<li>Check the SAPI or the environment to change how the bootstrap behaves</li>\n<li>Create a service container</li>\n<li>Instantiate or obtain a front controller</li>\n<li>Invoke the front controller</li>\n<li>Call <code>exit()</code>\n</li>\n</ul>\n<p>These look like reasonable concerns for a bootstrap script.</p>\n<p>Are there any bootstrap scripts that already work that way, or close to it? Yes:</p>\n<ul>\n<li><a href=\"https://github.com/auraphp/Aura.Web_Project/blob/master/web/index.php\">Aura</a></li>\n<li><a href=\"https://github.com/laminas/laminas-mvc-skeleton/blob/2.5.x/public/index.php\">Laminas-MVC</a></li>\n<li><a href=\"https://github.com/nette-examples/quickstart/blob/v4.0/www/index.php\">Nette</a></li>\n<li><a href=\"https://github.com/tempestphp/tempest-framework/blob/3.x/public/index.php\">Tempest</a></li>\n<li><a href=\"https://github.com/yiisoft/demo/blob/master/blog/public/index.php\">Yii</a></li>\n</ul>\n<p>These <em>almost</em> work that way ...</p>\n<ul>\n<li>\n<a href=\"https://github.com/laravel/laravel/blob/12.x/public/index.php\">Laravel</a> creates a request object;</li>\n<li>\n<a href=\"https://github.com/mezzio/mezzio-skeleton/blob/3.18.x/public/index.php\">Mezzio</a> makes two configuration calls;</li>\n<li>\n<a href=\"https://github.com/dracony/PHPixie-Sample-App/blob/master/web/index.php\">PHPixie</a> sends the response;</li>\n</ul>\n<p>... but otherwise limit themselves to the reduced set of bootstrap tasks.</p>\n<h3>III.</h3>\n<p>At this point, it looks like the major source of variation is how the bootstrap\nscripts obtain and run the front controller (usually called an <em>Application</em> or a\n<em>Kernel</em>) :</p>\n<pre><code class=\"language-php\">// Aura\n$kernel = (new \\Aura\\Project_Kernel\\Factory)-&gt;newKernel(...);\n$kernel();\n\n// Laminas-MVC\n$container = require __DIR__ . '/../config/container.php';\n$app = $container-&gt;get('Application');\n\n// Laravel\n$app = require_once __DIR__.'/../bootstrap/app.php';\n$app-&gt;handleRequest(Request::capture());\n\n// Mezzio\n$app = $container-&gt;get(Application::class);\n$app-&gt;run();\n\n// Nette\n$application = $container-&gt;getByType(Application::class);\n$application-&gt;run();\n\n// PHPixie\n$pixie = new \\App\\Pixie()-&gt;bootstrap($root);\n$pixie-&gt;bootstrap($root)-&gt;http_request()-&gt;execute();\n\n// Tempest\nHttpApplication::boot(...)-&gt;run();\n\n// Yii\n$runner = new HttpApplicationRunner(...);\n$runner-&gt;run();\n</code></pre>\n<p>On examination, we find three kinds of relationships between the front\ncontroller (<em>Application</em>) and the service <em>Container</em>:</p>\n<ul>\n<li>\n<p><strong>Combined:</strong> the <em>Application</em> extends the <em>Container</em>.</p>\n</li>\n<li>\n<p><strong>Service-located:</strong> the <em>Container</em> is created, then passed into the\n<em>Application</em> so the <em>Application</em> can locate services; alternatively, the\n<em>Application</em> itself creates and retains the <em>Container</em>.</p>\n</li>\n<li>\n<p><strong>Dependency-injected:</strong> the <em>Container</em> is created, then the <em>Application</em>\nis obtained from it.</p>\n</li>\n</ul>\n<p>All of the above variations look like they could be easily reconciled to use a\nfactory to create a service container, then obtain a front controller\nfrom the service container and run it.</p>\n<h3>IV.</h3>\n<p>With that one final adjustment toward a factory and dependency injection, we\nfind a prototypical set of bootstrap tasks:</p>\n<ol>\n<li>Require an autoloader.</li>\n<li>Instantiate a service container factory.</li>\n<li>Obtain the service container from the factory.</li>\n<li>Obtain a front controller from the service container.</li>\n<li>Run the front controller.</li>\n</ol>\n<p>This set of tasks should be achievable with little effort in any PHP system.\nHere is an example with hypothetical implementations of the <a href=\"https://star-interop.dev\">Star-Interop</a>\nstandard interfaces for <a href=\"https://github.com/ioc-interop/interface/blob/1.x/src/IocContainerFactory.php\"><em>IocContainerFactory</em></a>, <a href=\"https://github.com/ioc-interop/interface/blob/1.x/src/IocContainer.php\"><em>IocContainer</em></a>, and <a href=\"https://github.com/front-interop/interface/blob/1.x/src/FrontController.php\"><em>FrontController</em></a> ...</p>\n<pre><code class=\"language-php\">require dirname(__DIR__) . '/vendor/autoload.php';\n\nuse Project\\Bootstrap\\ContainerFactory;\nuse Project\\Presentation\\Http\\HttpFrontController;\n\nnew ContainerFactory()\n    -&gt;newContainer()\n    -&gt;getService(HttpFrontController::class)\n    -&gt;run();\n</code></pre>\n<p>... but of course using the Star-Interop standards is not a requirement. Any PHP\nsystem with an autoloader, container factory, service container, and front\ncontroller can follow this idiom.</p>\n<p>There are still three activities from the researched bootstrap scripts that are\nnot covered by the prototype ...</p>\n<ul>\n<li>Check the SAPI or the environment to change how the bootstrap behaves</li>\n<li>Load or set environment variables</li>\n<li>Call <code>exit()</code>\n</li>\n</ul>\n<p>... and those could reasonably be added to the bootstrap code. However, all\nother bootstrap script activity has been consolidated into the container\nfactory, container, or front controller.</p>\n<h3>V.</h3>\n<p>This approach also lends itself to command-line systems; we need only to swap\nthe HTTP front controller for a CLI front controller and add an <code>exit()</code>:</p>\n<pre><code class=\"language-php\">exit(\n    new ContainerFactory()\n        -&gt;newContainer()\n        -&gt;getService(CliFrontController::class)\n        -&gt;run()\n);\n</code></pre>\n<p>In conclusion, the prototypical design ...</p>\n<ul>\n<li>moves the bootstrap logic out of scripts and into classes as early as possible;</li>\n<li>eliminates the introduction of global variables and constants from the bootstrap; and,</li>\n<li>decouples the bootstrap script from any specific project directory structure (i.e.,\nthere is no need to require a script file from a particular location to get the\nservice container).</li>\n</ul>\n<p>In short, it standardizes a common approach to bootstrap scripts that is useful to, and recognizable in, any PHP system.</p>\n<h3>UPDATES</h3>\n<p>Mon 09 Feb 2026:</p>\n<ul>\n<li>\n<p>An earlier version of this article linked to the Slim skeleton; per\n<a href=\"https://old.reddit.com/r/PHP/comments/1qzh4mi/designing_a_bootstrap_script/o4bhbt2/?context=3\">commenter <code>equilini</code> on Reddit</a>\nit now links to the Slim documentation.</p>\n</li>\n<li>\n<p>Likewise, <a href=\"https://old.reddit.com/r/PHP/comments/1qzh4mi/designing_a_bootstrap_script/o4e8knf/?context=3\">commenter <code>dknx01</code> claimed confusion</a> regarding Joomla and Symfony links to the bootstrap scripts; those links have been made more explicit, along with the one for FlightPHP.</p>\n</li>\n</ul>\n"
}
