Welcome to Part 3 of the series! From here on, we’ll be focusing on the tools and techniques used to build modern, professional PHP applications. We’re going to start with the single most important tool in the modern PHP ecosystem: Composer.
So far, we’ve written all our code from scratch. But what if you need a powerful logging library, a tool to handle HTTP requests, or a library to generate fake data for testing? Building these yourself would take a huge amount of time. Composer is a dependency manager for PHP. It allows you to declare the libraries (or “packages”) your project depends on, and it will manage installing and updating them for you.
Crucially, Composer also provides a powerful autoloader. This means we no longer have to litter our files with require_once statements. We can simply use a class, and Composer will automatically find and load the correct file for us. This is a game-changer for building organized, large-scale applications.
First, you need to install Composer globally on your system so you can use the composer command from anywhere.
Follow the Official Instructions:
The installation process can change, so the best way to install Composer is to follow the official, up-to-date instructions on the Composer website.
Follow the command-line installation instructions for your operating system. For Linux and macOS, this typically involves running a few commands in your terminal. For Windows, there’s a handy Composer-Setup.exe you can download and run.
Verify the Installation:
Once the installation is complete, open a new terminal and run:
Terminal window
# Check Composer is installed and accessible
composer--version
Expected output:
Composer version 2.7.2 2024-04-20 12:30:42
The exact version number may differ. As long as you see Composer version followed by a version number, Composer is ready to go!
Solution 1: Make sure you opened a new terminal window after installation. The PATH changes don’t take effect in existing terminal sessions.
Solution 2: On Linux/macOS, verify Composer is in your PATH by running which composer. If nothing appears, you may need to move the composer binary to /usr/local/bin/ or add its location to your PATH environment variable.
Solution 3: On Windows, reboot your computer if reopening the terminal doesn’t work.
Problem: Permission errors during installation
Solution: On Linux/macOS, you may need to use sudo for the final installation step, or install Composer to a directory you have write access to.
Let’s start a new project for the simple blog we’ll be building in the upcoming chapters.
Create a Project Directory:
From your main coding folder, create a new directory for our blog application and navigate into it.
Terminal window
# Create and enter the project directory
mkdirsimple-blog
cdsimple-blog
Initialize the Project:
Run the composer init command. This will launch an interactive wizard to help you create your composer.json file.
Terminal window
# Start the interactive project setup
composerinit
You can accept the defaults for most of the questions by just pressing Enter, but fill in your own information for the author part. When it asks if you want to define dependencies interactively, say no for now.
Interactive prompts you’ll see:
Package name (<vendor>/<name>): your-name/simple-blog
Description []: A simple blog application
Author [Your Name <you@example.com>, n to skip]: (press Enter)
Minimum Stability []: (press Enter)
Package Type (e.g. library, project, metapackage, composer-plugin) []: (press Enter)
License []: (press Enter)
Would you like to define your dependencies (require) interactively [yes]? no
Would you like to define your dev dependencies (require-dev) interactively [yes]? no
Add PSR-4 autoload mapping? Maps namespace "YourName\SimpleBlog" to the entered relative path. [src/, n to skip]: n
Verify the Result:
After the wizard is done, you will have a new composer.json file in your directory. View it:
Terminal window
# Display the contents of composer.json
catcomposer.json
Expected output:
{
"name": "your-name/simple-blog",
"description": "A simple blog application",
"authors": [
{
"name": "Your Name",
"email": "you@example.com"
}
],
"require": {}
}
This JSON file describes your project. The empty require object will soon contain your dependencies.
Why These Fields Matter:
name: Identifies your package if you ever publish it to Packagist.
authors: Credits the creators.
require: Lists production dependencies (libraries your app needs to run).
require-dev: Lists development dependencies (tools for testing, debugging, etc. – we’ll use this later).
Found the latest compatible version of Monolog on Packagist.
Downloaded the library and its dependencies into a new vendor/ directory.
Updated your composer.json file to include Monolog in the require section.
Created a composer.lock file, which records the exact versions of all packages installed. This ensures everyone on your team uses identical versions.
Generated the autoloader files inside vendor/composer/.
Verify the Changes:
Check your composer.json file:
Terminal window
# View the updated composer.json
catcomposer.json
You should now see:
{
"name": "your-name/simple-blog",
"require": {
"monolog/monolog": "^3.5"
}
}
The ^3.5 is a version constraint using semantic versioning. It means “any version >= 3.5.0 but < 4.0.0”.
Important: You should commit composer.json and composer.lock to version control (like Git), but ignore the vendor/ directory. Other developers run composer install to download the exact dependencies from composer.lock.
Use the Package:
Create a new file index.php and add the following code to see the autoloader in action.
File: index.php
<?php
// This one line is all we need to activate Composer's autoloader.
require_once'vendor/autoload.php';
// Now we can `use` the classes from our installed package.
Problem: Fatal error: Uncaught Error: Class "Monolog\Logger" not found
Solution: Make sure you included require_once 'vendor/autoload.php'; at the top of your PHP file. The autoloader must be loaded before you can use any Composer packages.
Problem: Warning: require_once(vendor/autoload.php): Failed to open stream
Solution: You’re running the script from the wrong directory. Make sure you’re in the simple-blog directory where the vendor/ folder exists. Use pwd (Linux/macOS) or cd (Windows) to check your current directory.
Problem: Composer takes a very long time or times out
Solution: This is usually a network issue. If you’re behind a corporate proxy, you may need to configure Composer to use it. See the Composer proxy documentation.
Step 4: Autoloading Your Own Code with PSR-4 (~6 min)
The real power comes when we use Composer to autoload our own application’s classes. The standard for this is PSR-4. It’s a convention that maps a namespace prefix to a directory.
Let’s set up a src/ directory for our app’s code, with the namespace App.
Create a src/ Directory:
Create a new directory named src in your project root, along with a Models subdirectory.
Terminal window
# Create the directory structure for our application code
mkdir-psrc/Models
Configure composer.json:
Add an autoload section to your composer.json file to map the App\\ namespace prefix to the src/ directory.
File: composer.json
{
"name": "your-name/simple-blog",
"authors": [
{
"name": "Your Name",
"email": "you@example.com"
}
],
"require": {
"monolog/monolog": "^3.5"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
Understanding the Mapping:
"App\\" → "src/" means: any class with namespace App\Something will be found in src/Something.php
The namespace structure must exactly match the directory structure.
Update the Autoloader:
After changing composer.json, you must regenerate the autoloader files.
Terminal window
# Regenerate the autoloader to include our new mapping
composerdump-autoload
Expected output:
Generating autoload files
Generated autoload files
Create a Namespaced Class:
Now, create a new class inside the src/ directory that follows the PSR-4 mapping.
Namespace: App\Models
Class: User
File location: src/Models/User.php
File: src/Models/User.php
<?php
namespace App\Models;
classUser
{
publicstring$name;
publicfunction__construct(string$name)
{
$this->name=$name;
}
publicfunctiongreet():string
{
return"Hello, my name is {$this->name}!";
}
}
Note: This code uses typed properties (public string $name) and return type declarations (public function greet(): string), which are fully supported in PHP 8.4. If you see these type declarations in modern PHP code, they help prevent bugs by ensuring variables contain the expected data types.
Use Your Class:
Update your index.php to use your new User class alongside Monolog.
$logger->info("A new user was created: ".$user->name);
echo$user->greet() .PHP_EOL;
Run and Verify:
Execute the updated script:
Terminal window
# Run the script
phpindex.php
Expected output:
Hello, my name is Dale!
Perfect! Composer now knows how to find and load both third-party packages and your own application’s classes, as long as you follow the PSR-4 namespace-to-directory structure.
Tip: Git Best Practices
If you’re using Git for version control, create a .gitignore file in your project root with this content:
/vendor/
/app.log
This ensures you don’t commit the vendor/ directory or log files. Other developers will run composer install to get their own vendor/ folder with the exact same versions specified in composer.lock.
Not all packages are needed in production. Testing frameworks, code quality tools, and debugging utilities are only needed during development. Composer separates these into a require-dev section.
Install a Development Dependency:
Let’s add PHP_CodeSniffer, a tool that checks your code against coding standards like PSR-12.
Terminal window
# Install as a dev dependency with the --dev flag
composerrequire--devsquizlabs/php_codesniffer
Expected output:
Using version ^3.8 for squizlabs/php_codesniffer
./composer.json has been updated
Running composer update squizlabs/php_codesniffer
Loading composer repositories with package information
If your code follows PSR-12, you’ll see no output (which means success!). Otherwise, you’ll see specific style violations to fix.
Why This Matters:
When deploying to production, you can run composer install --no-dev to skip development dependencies, making your production vendor/ folder smaller and faster. This separation keeps your production environment lean.
One of Composer’s biggest benefits is ensuring everyone on a team uses the exact same dependency versions. Let’s simulate joining an existing project.
Simulate a Fresh Clone:
Let’s pretend you’re a new developer joining the team. You’ve cloned the repository (which includes composer.json and composer.lock) but not the vendor/ directory.
Terminal window
# Delete the vendor directory to simulate a fresh clone
rm-rfvendor/
# Verify it's gone
ls-la
You should see composer.json and composer.lock but no vendor/ folder.
Install Dependencies:
When you have a composer.lock file, use composer install (not require). This installs the exact versions recorded in the lock file:
Terminal window
# Install exact versions from composer.lock
composerinstall
Expected output:
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Verify Everything Works:
Run your application to confirm the dependencies were installed correctly:
Terminal window
# Test the application
phpindex.php
Expected output:
Hello, my name is Dale!
Perfect! Your environment now matches exactly what every other developer on the team has.
The Workflow:
Adding a new dependency: Use composer require <package> — this updates both composer.json and composer.lock. Commit both files.
Joining a project or pulling changes: Use composer install — this reads composer.lock to install exact versions.
Updating dependencies: Use composer update — this updates to newer versions within constraints and updates composer.lock. Commit the updated lock file.
Problem: composer install says “Your lock file is out of date”
Solution: Someone updated composer.json without running composer update to regenerate the lock file. Run composer update to sync them, then commit the updated composer.lock.
Solution: The project requires a different PHP version than you have. Check composer.json for the "php" constraint in the require section. Install the required PHP version using Chapter 0.
Problem: After running composer install, tests/scripts fail
Solution: You might have skipped dev dependencies. Make sure you ran composer install (which includes dev dependencies) not composer install --no-dev (which is only for production).
Add another package: Install the vlucas/phpdotenv package (used for managing environment variables). Add it with composer require vlucas/phpdotenv and read its documentation on Packagist to create a simple .env file and load it.
Create more classes: Add a Post class in App\Models\Post with properties for title, content, and author. Use it in index.php.
Organize further: Create a src/Services/ directory and add a LoggerService class that wraps Monolog setup. Use the namespace App\Services\LoggerService and instantiate it in your index.php.
Explore Packagist: Visit packagist.org and browse popular packages. Check out guzzlehttp/guzzle (HTTP client), faker/faker (fake data generator), or symfony/console (CLI tool builder).
Version constraints: In your composer.json, change the Monolog constraint from ^3.5 to ~3.5.0 and run composer update. Research what ^, ~, and exact versions mean in semantic versioning.
This was a huge leap into the world of modern PHP development. You now understand how to use Composer, the cornerstone of the entire ecosystem. You can initialize a project, pull in powerful third-party libraries, and set up a professional, PSR-4 compliant autoloader for your own code. The days of manual require statements are over!
What you accomplished:
✅ Installed Composer globally
✅ Created a new Composer project with composer.json
✅ Installed and used a third-party package (Monolog)
✅ Configured PSR-4 autoloading for your own classes
✅ Separated development and production dependencies with require-dev
✅ Learned the composer install workflow for team collaboration
✅ Built a working project structure ready for expansion
In the next chapter, we’ll put this to use and start interacting with the filesystem in a more structured way using Composer-managed libraries.
::: info Code Examples
Complete, runnable examples from this chapter are available in:
example-project/ - Complete Composer project with dependencies
solutions/ - Solutions to chapter exercises
The example project includes a working composer.json, autoloading setup, and demonstrates using third-party packages.
:::