# Requirements
The framework is plain PHP with no composer dependency: what it needs is a PHP runtime and a web server that
can rewrite urls. Nothing else — the client's javascript and stylesheets are compiled in PHP too.
## PHP 8.3 or newer
`mmo.core` uses **typed class constants** (`public const string LANGUAGE_REGEX = …` in `src/I18n.php`),
which is PHP 8.3 syntax, so 8.2 will not even parse the source. 8.4 is what it is developed on.
These extensions are used:
| extension | what needs it |
|---|---|
| `mbstring` | string handling across `mmo.core` |
| `json` | configuration files, `JsonRepository`, every json response |
| `curl` | `MMO\core\server\Curl`, and anything fetching a remote url |
| `openssl` | only DKIM signing of outgoing mail (`MMO\core\server\Mail`) |
| `zip` | archives (`MMO\core\files\*`) and the `xlsx` document writer |
Nothing here needs a database: the demo persists to plain json files through `JsonRepository`.
## A web server with url rewriting
Every package is served from **its own root**, not from its `demo/www` folder — the `.htaccess` at the root
of the package rewrites everything onto `demo/www/index.php` and tells it where the real document root is:
```apache
SetEnv MMO_MODE_REWRITE demo/www
RewriteRule ^ demo/www/index.php [L]
```
So Apache needs `mod_rewrite` enabled **and** `AllowOverride All` on the folder the repository sits in,
otherwise the `.htaccess` is ignored and every url 404s. `libs/client/demo/www/bin` must be writable by the
server user: the compiled javascript, css and icon sprite are written there on the fly.
## No Java, and no node
`mmo.client` used to compile its javascript with Google Closure, a jar that needed a JRE on the `PATH`. It
does not any more: `MMO\client\JSCompiler` bundles the sources in pure PHP — it reads the
`MMO.provide()`/`MMO.require()` declarations at the top of each file to order them, strips them, and writes
the bundle with a source map. There is nothing to install and nothing to turn off.
Node is only needed to run the javascript unit tests (`npm test` in `libs/client`), never to build.
> Dart Sass is **not** required any more. Every element in `src/customElements` now styles itself in
> [Simple CSS](../scs/overview), which is compiled in pure PHP. `MMO\client\DartSass` survives only for an
> application that still ships `<style lang="sass">`.
## Git
The framework is one repository with every library as a submodule, so a plain `git clone` without
`--recurse-submodules` gives you empty folders. See [Getting the framework](installation?guide=50-framework.md).
# Installing on Linux
Ubuntu/Debian recipe. What matters is PHP 8.3+, Apache with `mod_rewrite` and `AllowOverride All` over the
folder you clone into — see [Requirements](installation?guide=10-requirements.md) for why.
## Packages
```bash
# prerequisites
sudo apt update -qq
sudo apt install -qy software-properties-common
# php 8.3 (ondrej's ppa is still the way to get a current php on ubuntu)
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update -qq
sudo apt install -qy php8.3 php8.3-common php8.3-cli php8.3-mbstring php8.3-zip php8.3-curl
# apache, git
sudo apt install -qy apache2 git unzip
# apache modules the framework relies on
sudo a2enmod rewrite
sudo a2enmod ssl
sudo systemctl restart apache2
```
Add yourself and the server user to each other's group, so both can write the generated files:
```bash
sudo usermod -a -G www-data $(whoami)
sudo usermod -a -G $(whoami) www-data
```
> You have to log out and back in for a new group to take effect.
## Virtual host
Serve the folder you keep your checkouts in — **not** a package's `demo/www`. Each package's own
`.htaccess` does the rewriting, which is why `AllowOverride All` is not optional here.
Create `/etc/apache2/sites-available/000-default.conf`:
```apache
<VirtualHost *:80>
DocumentRoot "/home/USERNAME/dev"
ServerName localhost
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory "/home/USERNAME/dev">
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
```
```bash
sudo a2ensite 000-default.conf
sudo systemctl restart apache2
```
> `Options FollowSymLinks` earns its place: a package's assets are often symlinked into its `demo/www`.
Check what you got:
```bash
php -v # 8.3 or newer
php -m # mbstring, zip, curl, json listed
```
Then go on to [Getting the framework](installation?guide=50-framework.md).
# Installing on Windows
WampServer bundles Apache and PHP, which is everything the framework needs on Windows apart from git.
See [Requirements](installation?guide=10-requirements.md) for what each piece is for.
## WampServer
1. Install the **Visual C++ redistributables** first — Wamp will not start without them:
[2015-2022 x64](https://aka.ms/vs/17/release/vc_redist.x64.exe) /
[2015-2022 x86](https://aka.ms/vs/17/release/vc_redist.x86.exe).
Wamp's installer also checks for the older VC11/VC13 packages; the
[Wamp download page](https://www.wampserver.com/en/download-wampserver-64bits/) lists whichever it wants
for the version you are taking.
2. Install [WampServer 64 bits](https://www.wampserver.com/en/download-wampserver-64bits/).
3. Left-click the tray icon and set **PHP > Version** to 8.3 or newer.
4. Left-click the tray icon and check **Apache > Apache modules > rewrite_module**.
5. Open `http://localhost/`. The Wamp homepage means Apache and PHP are running.
Projects live under `C:\wamp64\www`, served as `http://localhost/<folder>`.
## AllowOverride
Wamp ships `AllowOverride All` for `C:\wamp64\www`, so the packages' `.htaccess` files are read as they
are. If you serve the repository from somewhere else, grant it there too — without it every url 404s,
because nothing rewrites onto `demo/www/index.php`.
## phar.readonly
Only needed if you are going to **build** a package as a `.phar` (`mmo.json`'s `build` entry). Left-click
the tray icon, **PHP > php.ini**, set `phar.readonly = 0`, save, restart Wamp. Running the demo does not
need it.
## Git
Install [Git for Windows](https://git-scm.com/download/win), then follow
[Git and SSH](installation?guide=40-git-and-ssh.md) and
[Getting the framework](installation?guide=50-framework.md).
---
## If Wamp will not start
*"The code execution cannot proceed because MSVCP120.dll / MSVCR120.dll was not found"* — a Visual C++
redistributable is missing or was installed after Wamp. Uninstall Wamp, delete `C:\wamp64`, install every
redistributable listed above, then install Wamp again.
# Git and SSH
The framework is cloned over SSH, so a key has to be on your GitHub account before anything else works.
## Git configuration
```bash
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --list
```
`safe.directory` is the one that bites on a shared checkout: git refuses to work in a repository owned by
another user. A development machine where the files belong to `www-data` as often as to you is exactly that
case, so set it system-wide:
```bash
sudo git config --system safe.directory '*'
```
## SSH keys
```bash
ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub
```
Paste the **public** key into [github.com/settings/keys](https://github.com/settings/keys), then load the
**private** one into your agent:
```bash
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh -T git@github.com # "Hi <you>! You've successfully authenticated"
```
On Windows, Git for Windows installs the same `ssh-keygen`; run it from Git Bash.
## Access
The libraries are private repositories under [MarcMouret](https://github.com/MarcMouret). Ask for access
before cloning — a submodule you cannot read fails the clone halfway through, with a permission error that
does not say which submodule it was.
# Getting the framework
One repository, every library a submodule of it. Clone it into whatever folder your web server serves —
`/home/USERNAME/dev` on Linux, `C:\wamp64\www` on Windows — and the urls below will work as written.
## Clone
```bash
cd ~/dev
git clone --recurse-submodules git@github.com:MarcMouret/MMOFramework.git MMO
```
`--recurse-submodules` is not optional: without it `libs/` is a set of empty folders. On a clone that was
already made without it:
```bash
cd MMO
git submodule update --init --recursive
```
What you get under `libs/` is one folder per package — `core`, `client`, `doc`, `pdo`, `oauth`, `mongodb`,
`edms`, `pdf`, `git`, `admin`, `editor`, `math`, `ocr`, `rpg` — each its own repository with its own
branches. `git submodule foreach git status` is the quickest way to see where they all sit.
> Submodules are pinned to a commit, not to a branch. After `git pull`, run
> `git submodule update --recursive` or you are running the code of whatever commit the parent recorded.
## Run a package's demo
Every package is served **from its own root**, because that is where its `.htaccess` lives. So the url is
your document root, plus the path of the clone, plus `libs/<package>` — with the clone above named `MMO`
directly under the folder Apache serves:
```
http://localhost/MMO/libs/client the client's demo, the site you are reading
http://localhost/MMO/libs/core mmo.core's demo
http://localhost/MMO/libs/doc mmo.doc's demo
```
Clone it somewhere else and every url moves with it: a checkout kept as `MMO/master` serves the same site
at `http://localhost/MMO/master/libs/client`. Nothing in the framework hardcodes that prefix — it reads
its own base from the request, which is why a guide names a path relative to the package root rather than
a full url.
The `.htaccess` sets `MMO_MODE_REWRITE demo/www` and rewrites everything that is not an existing asset onto
`demo/www/index.php`. If you get a directory listing or a 404 instead of a page, the `.htaccess` is being
ignored: `AllowOverride All` is missing, or `mod_rewrite` is not enabled.
## The bin folder
`demo/www/bin` holds what the framework generates: `mmo.client.js`, `mmo.client.css`, the compiled `.jhs`
bundle and the icon sprite. It is a cache — it is rebuilt from source whenever a source file is newer, and
deleting it is the correct fix for anything that looks stale.
```bash
rm -rf libs/client/demo/www/bin/*
```
The server user has to be able to write there. If the page loads with no styling and no icons, that is
almost always what happened:
```bash
chmod -R 775 libs/client/demo/www/bin
```
## Development mode
The demo's entry point (`demo/www/index.php`) turns on debugging before anything else runs:
```php
define('MMO_DEBUG', 'info+');
define('MMO_MODE', 'dev');
```
`MMO_MODE` is `dev` or `prod`, and defaults to `dev` when nothing defines it. In `dev` the theme rebuilds
a `bin` file as soon as a source file is newer than it (`Theme.php`), the compilers emit source maps,
and an uncaught exception is printed into the page instead of being swallowed. A deployed site sets `prod`.
# Debugging with Xdebug
Xdebug is a PHP extension that gives you breakpoints, stack traces and a profiler. PHP 8.3 and 8.4 run
**Xdebug 3**, whose settings are not the ones you will find in most older guides: every `xdebug.remote_*`
option was removed in 3.0, and so was `xdebug.profiler_enable`. What replaced all of them is a single
`xdebug.mode`.
## Install
Linux:
```bash
sudo apt install -qy php8.3-xdebug
php -v # the banner names the Xdebug version
```
Windows: take the DLL matching your PHP version and architecture from the
[Xdebug wizard](https://xdebug.org/wizard) (paste `php -i` output into it and it tells you exactly which
file), drop it in Wamp's `php/ext` folder, and add `zend_extension` to `php.ini` as below.
## Configure
On Linux, `/etc/php/8.3/mods-available/xdebug.ini`; on Windows, the `php.ini` Wamp opens for you (the
`zend_extension` line is only needed on Windows — the Debian package writes its own).
```ini
zend_extension = xdebug ; windows: zend_extension = "C:\wamp64\bin\php\php8.3.x\ext\php_xdebug.dll"
xdebug.mode = debug ; debug | profile | trace | develop | coverage, comma-separated
xdebug.start_with_request = trigger
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9003
xdebug.log = /var/www/xdebug/xdebug.log
xdebug.output_dir = /var/www/xdebug
```
- `xdebug.mode` **is the whole switch.** `debug` for breakpoints, `profile` for cachegrind files,
`develop` for readable `var_dump`s and stack traces on errors. Several at once: `debug,develop`.
- `start_with_request = trigger` **means Xdebug only attaches when asked** — a `XDEBUG_TRIGGER` cookie or
query parameter, which is what the browser extensions set. `yes` attaches to every request and makes the
whole site slow; `no` never does.
- `client_port` **is 9003** in Xdebug 3, not 9000 and not 9005. Your IDE has to be listening on the same
port.
The log and output folders have to be writable by the server user:
```bash
sudo mkdir -p /var/www/xdebug
sudo chown www-data:www-data /var/www/xdebug
```
Restart the server after any change (`sudo systemctl restart apache2`, or Wamp's *Restart All Services*),
then check it took:
```bash
php -i | grep xdebug.mode
```
## From the command line
CLI reads the same ini, so a one-off run overrides what it needs:
```bash
php -d xdebug.mode=profile -d xdebug.start_with_request=yes script.php
php -d xdebug.mode=debug -d xdebug.start_with_request=yes script.php
```
Worth an alias:
```bash
alias phpp='php -d xdebug.mode=profile -d xdebug.start_with_request=yes'
```
On Windows, the same thing as a `phpp.bat` somewhere on the `PATH`:
```bat
@echo off
php -d xdebug.mode=profile -d xdebug.start_with_request=yes %*
```
## Profiling a page
Set `xdebug.mode = profile` with `start_with_request = trigger`, then load the page with
`?XDEBUG_TRIGGER=1`. A `cachegrind.out.*` file lands in `xdebug.output_dir`; open it with
[KCachegrind](https://kcachegrind.github.io/) or PhpStorm's profiler.
> Profiling writes a file per request and they are large. Leave the mode on `debug` (or off) the rest of
> the time.