PHP & Databases
Debian Development Machine Setup
Chapter 7 ยท PHP, Composer & Databases
Your web projects run on PHP and MySQL, so devserver needs both. The plan follows the pattern
from the last two chapters: check what is there, install the right thing, and keep a clear line between the
development machine and the deployment target. There is one trap in the PHP package names that this chapter
steers you around, and one surprise about “MySQL” on Debian.
Check First
On a fresh desktop install none of these should be present. apt policy shows what Debian 13
offers: PHP 8.4 (the current build is 8.4.26), Composer 2.8.8, and MariaDB
11.8.6.
Installing PHP Without a Web Server
Here is the trap. The obvious command, sudo apt install php, installs a metapackage that depends
on php8.4, and php8.4 in turn requires one of a server-side interpreter:
the Apache module, php8.4-fpm or php8.4-cgi. On a typical install, that means
apt pulls in Apache and starts a web server you did not ask for. On a development machine where you will use
PHP from the command line and its built-in test server, you do not want that.
php metapackage herephp is designed for someone setting up a web server. Install php-cli and
the extensions you need instead. Read apt's list of packages to be installed before you answer y:
if it includes apache2 or libapache2-mod-php8.4, you have installed the wrong thing.
Chapter 3's apt policy and apt's own summary are how you catch this.
| Package | Provides |
|---|---|
| php-cli | The php command, without any web server. |
| php-mbstring | Multi-byte string handling, needed for UTF-8 text including Japanese. |
| php-xml | XML and DOM support. Many frameworks and tools require it. |
| php-curl | HTTP requests to other services. |
| php-zip | Reading and writing zip archives. Composer uses it. |
| php-intl | Internationalisation support, needed by some libraries. |
| php-mysql | The mysqli and PDO MySQL drivers, for talking to MySQL and MariaDB. |
| php-sqlite3 | The SQLite driver, for lightweight local databases. |
Verify with the same checks you have used since Chapter 3:
php -m lists the loaded modules; the ones you asked for should all appear. type -a php
should show a single /usr/bin/php.
Trying it without a web server
PHP has a small development web server built in. Create a folder with an index.php and serve it:
Open http://localhost:8000 in a browser. Stop the server with Ctrl+C. The
built-in server is only for development: it is single-purpose and not meant for real traffic, which is what
the web server on debserver is for. Because it listens on localhost, nothing else on
your network can reach it.
Matching the server
Your web server still runs an older Debian, so its PHP will differ from devserver's 8.4. On
debserver, run php -v and note the version. When you reinstall it with Trixie, the
two will match. Until then, write code that runs on both, and test on the server before you trust it. If you
ever genuinely need a different PHP version alongside 8.4, Debian's own repositories cannot supply it, and
the usual options are a third-party repository (Linux Package Managers 3 explains how to add one safely and
what you are trusting when you do) or a container, as in the Docker for Beginners course.
Composer
Composer is PHP's dependency manager, the equivalent of npm (Chapter 6) and pip (Chapter 5). Debian packages it, and installing it that way is simple and keeps it managed by apt:
Composer recommends git and unzip, which is why they are in the command: Composer
downloads many packages as archives and works better with both present. You have already seen the pattern
for project dependencies:
| File / folder | What it is | Commit it? |
|---|---|---|
composer.json | The project's list of wanted packages and version ranges. | Yes |
composer.lock | The exact version of every package installed. | Yes |
vendor/ | The installed packages, plus an autoloader. | No (add to .gitignore) |
To use an installed package in your code, include Composer's autoloader once at the top of your script:
require __DIR__ . '/vendor/autoload.php';. As with npm ci, the important distinction
is between composer install and composer update. Install reads
composer.lock and installs exactly those versions, so it gives the same result everywhere.
Update ignores the lock, fetches the newest versions that composer.json allows,
and rewrites the lock. Use install to rebuild, and update only when you mean to upgrade.
composer require complains that a package needs a PHP extension you have not installed,
read the message: it names the extension. Install the matching php-NAME package with apt, then
run the command again.
Databases: What “MySQL” Means on Debian
Here is the surprise. Oracle's MySQL is not in Debian's repositories. On Trixie there is no
package called mysql-server. Instead there are metapackages called
default-mysql-server and default-mysql-client, which install MariaDB
(version 11.8.6), a fork of MySQL that is compatible for nearly everything a PHP project does. If you have
been running a database on Debian and calling it MySQL, it is very likely MariaDB already. Check on
debserver with mysql --version or mariadb --version: MariaDB announces
itself in the output.
devserver and debserver are Debian, they will both use MariaDB from
Debian's repositories, so using it on devserver keeps your development database as close as
possible to your real one. Differences in the version number will shrink once the server is reinstalled with
Trixie.
Installing and securing a local database
The server should listen only on 127.0.0.1, meaning only programs on devserver can
connect. Confirm that with ss, rather than assuming it. If a database is listening on
0.0.0.0, it is open to your whole network, and that is not what you want on a development box.
On Debian, MariaDB's administrator account is tied to your operating-system login through a Unix socket, so
sudo mariadb gets you in as the administrator without a database password. Older tutorials use
mysql as the command name; on current MariaDB, mariadb is the command to use
(a mysql name may also work through Debian's compatibility packages, but do not rely on it).
Use the administrator account to create a database and a separate, limited user for your projects:
The dev user can do anything inside devdb and nothing anywhere else, which limits the
damage a bug or a stray script can do. Test it from PHP using PDO, the database interface you will use in most
projects. Save this as db-test.php and run php db-test.php:
.gitignore, and
never commit them. Notice the query uses a prepared statement (prepare and
execute) with a placeholder instead of building SQL from text; that habit is what protects you
from SQL injection. The Personal Catalogue: PHP & MySQL course builds a full application on this same
pattern.
Starting and stopping it
A database that runs all day uses memory and starts on every boot. With 64 GB of RAM that is no burden, but you may prefer to run it only when needed:
SQLite: a database with no server
For small tools and quick experiments, you do not need a database server at all. SQLite stores a whole database in a single file:
PHP reaches it through the php-sqlite3 extension you already installed, using PDO with a
connection string like sqlite:/path/to/dev.db. Choose SQLite for something small and
self-contained, and MariaDB when you need to match the server's database.
Which Tool for Which Job
| You want to… | Use |
|---|---|
| Run PHP scripts and try a site locally | php-cli and php -S localhost:8000 |
| Add PHP libraries to a project | composer require (recorded in composer.lock) |
| Rebuild a project's libraries exactly | composer install |
| Match the server's database | MariaDB (mariadb-server) |
| A small, serverless database | SQLite (sqlite3, php-sqlite3) |
| A different PHP version | A third-party repository or a container |
Hands-On Exercises
Install PHP the correct way (without the php metapackage). Verify the version and the loaded extensions, confirm no web server was installed, and serve a page with the built-in server. Explain why apt install php would have been the wrong command here.
Create a Composer project, require a package, write a script that uses it via the autoloader, then delete vendor/ and restore it with composer install. Explain the difference between composer install and composer update, and say which files you would commit.
Install MariaDB, prove it listens only on localhost, create a database and a limited user, and write a PDO script that inserts and reads a row. Then record the PHP and database versions on debserver and say what differences you expect from devserver and how you would deal with them.
Chapter 7 Quick Reference
- Debian 13: PHP 8.4 (8.4.26), Composer 2.8.8, MariaDB 11.8.6
- Do not install the
phpmetapackage on a dev machine; it requires a server interpreter (Apache module, FPM or CGI) - Install
php-cli php-mbstring php-xml php-curl php-zip php-intl php-mysql php-sqlite3 php -v,php -mto check;php -S localhost:8000is a development-only web server- Composer: commit
composer.jsonandcomposer.lock, notvendor/;composer installuses the lock,composer updaterewrites it - Oracle MySQL is not packaged in Debian;
default-mysql-serverinstalls MariaDB sudo mariadbgives administrator access through your Unix login; use themariadbcommand, notmysql- Check the database listens on
127.0.0.1only:ss -tlnp | grep 3306 - Give each project a database and a limited user; use prepared statements; keep passwords out of Git
- SQLite (
sqlite3,php-sqlite3) is a database in one file, with no server - A different PHP version needs a third-party repository or a container