Index
# Starting a new application An application built on MMOFramework is a folder of its own with a manifest, a bootstrap and an entry point. It is not a composer package: nothing is installed with `composer require`, and no PSR-4 autoloader looks for its classes. ## The one command From a checkout of the framework (the hub, the clone that holds `libs/`): ```sh php tools/mmo.php project ../acme.portal --name=acme.portal --namespace=AcmePortal php tools/mmo.php project ../acme.portal --dry-run # say what it would write, write nothing ``` It writes the whole tree below, ready to run and ready to deploy with mmo.admin. The parent folder has to exist already. What follows is what it writes, and why — write it by hand only if you cannot run the command. ## The tree ``` acme.portal/ bootstrap.php shared by the web entry point, the scripts and the tests mmo.json the manifest configs/ config.json every mode dev.json a checkout only (never in a release) prod.json read from the target's shared/ folder local.json this machine's own values -- gitignored, never committed, never shipped src/ bootstrap.php requires every class file, and registers the namespace as a path pages/ styles/ www/ index.php the web entry point; www/ is a release's document root tests/ bootstrap.php unit/ phpunit.xml .htaccess rewrites every request that is not a file to www/index.php ``` ## The root `bootstrap.php` It decides the **mode**, then loads the framework, then the configuration. **The mode follows the tree, not the host name.** A release is a `git archive` of one commit, and never contains `src/` — `.gitattributes` export-ignores it — because it runs from the `.phar` packages built into `packages/`. So `src/` exists in a developer's checkout and nowhere else, which holds on a laptop, on a server, and on the command line where there is no host name to ask anyway. ```php fragment if (!defined('MMO_MODE')) {define('MMO_MODE', is_dir(__DIR__ . '/src')? 'dev' : 'prod');} //a checkout runs on the libraries' sources (`libs` links to the `libs/` folder of an MMOFramework clone); a //release runs on the packages mmo.admin built into it, and core, loaded from its phar, finds the others beside it if (MMO_MODE === 'dev') {require(__DIR__ . '/libs/core/src/bootstrap.php');} else {require(__DIR__ . '/packages/mmo.core.phar');} ``` Then the configuration, layered — each file overriding the one before: ```php fragment \MMO\config( [ //both are otherwise derived from the running script, which phpunit, or a script outside this folder, is not \MMO\core\Application::class => ['paths' => ['root' => __DIR__]], \MMO\core\PackageManager::class => ['project' => __DIR__ . '/mmo.json'], ], __DIR__ . '/configs/config.json', __DIR__ . '/configs/' . MMO_MODE . '.json' ); //this machine's own values (a local database password): gitignored, so never committed, and never shipped if (is_file(__DIR__ . '/configs/local.json')) {\MMO\config(__DIR__ . '/configs/local.json');} ``` ## `src/bootstrap.php` The package bootstrap, run by <@\MMO\core\PackageManager::inc> when the project is loaded. **Nothing autoloads on its own**: every class file of `src/` is required here, after whatever it extends or uses. Registering the namespace as a path is what makes the namespace an alias of this folder — `src/` in a checkout, and the same folder inside `packages/.phar` in a release, which has no `src/` at all. ```php fragment namespace AcmePortal; require(__DIR__ . '/entities/Article.php'); //before whatever extends or uses it require(__DIR__ . '/controllers/ArticlesController.php'); return [ \MMO\core\Application::class => ['paths' => [__NAMESPACE__ => __DIR__]], ]; ``` That alias is why a configuration says `{AcmePortal}/pages` and **never** `{root}/src/pages`: the second one resolves to nothing in a release: `.gitattributes` export-ignores `src/`, and mmo.admin refuses an artifact that contains one. ## `www/index.php` Everything is set up by `../bootstrap.php`, so the entry point only starts the application: ```php fragment require(__DIR__ . '/../bootstrap.php'); \MMO\run(); ``` `.htaccess` sends every request that is not a file there. A controller is reached because it is listed under the Application's `routes` configuration (or connected with `connectRoutes()`), not because it was found by scanning. ## `mmo.json` @see <@\MMO\core\PackageManager> for every key. The minimum: ```json { "name": "acme.portal", "version": "1.0.0", "namespace": "AcmePortal", "sources": ["src"], "dependencies": {"mmo.core": "20.*"}, "bootstrap": "src/bootstrap.php" } ``` ## Next - Tests: @see Testing.md - Deploying: mmo.admin's `deploy.json`, `php scripts/admin.php plan` then `release`. - A new `mmo.*` library rather than an application: @see NewLibrary.md