← Back to Index

Chapter 7: Libraries & Tools

Composer Ecosystem & Practical Libraries

1 Composer in Depth

Composer is the dependency management tool for PHP and the cornerstone of the entire modern PHP ecosystem. It handles downloading and installing third-party packages and automatically generates autoload files.

🔄 Cross-Language Comparison: Composer is to PHP what npm is to Node.js, pip is to Python, and Maven/Gradle is to Java. The config file composer.json is equivalent to package.json / requirements.txt. The lock file composer.lock is equivalent to package-lock.json.

Common Commands

# 初始化项目(交互式生成 composer.json)
composer init

# 安装一个包(自动写入 composer.json 并下载)
composer require guzzlehttp/guzzle

# 安装开发依赖(仅开发环境使用)
composer require --dev phpunit/phpunit

# 根据 composer.lock 安装所有依赖(部署时使用)
composer install

# 更新依赖到最新兼容版本
composer update

# 更新 autoload 映射(添加新类后)
composer dump-autoload

composer.json Structure

{
    "name": "myvendor/myproject",
    "description": "我的 PHP 项目",
    "type": "project",
    "require": {
        "php": ">=8.4",
        "guzzlehttp/guzzle": "^7.8",
        "monolog/monolog": "^3.0",
        "nesbot/carbon": "~3.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^11.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}

Version Constraints

Symbol Meaning Example
^ Compatible update (won't break major version) ^7.8>=7.8.0 <8.0.0
~ Allow last digit to grow ~3.0>=3.0.0 <4.0.0
* Wildcard 3.* → any 3.x version
Exact version Lock to specific version 3.7.2

PSR-4 Autoloading

After configuring autoload.psr-4, you only need to include vendor/autoload.php and all classes will be autoloaded:

<?php
// 项目入口文件
require __DIR__ . '/vendor/autoload.php';

// 直接使用,无需手动 require 每个文件
use App\Services\UserService;
use App\Models\User;

$service = new UserService();
$user = $service->findById(1);

Directory mapping: namespace App\Services\UserService maps to file src/Services/UserService.php.

2 Guzzle — HTTP Client

Guzzle is the most popular HTTP client library for PHP, providing a clean API for sending HTTP requests, handling responses, and supporting async concurrency.

composer require guzzlehttp/guzzle

Basic Usage

<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$client = new Client([
    'base_uri' => 'https://api.example.com',
    'timeout'  => 5.0,
]);

// GET 请求
$response = $client->get('/users', [
    'query' => ['page' => 1, 'limit' => 10],
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept' => 'application/json',
    ],
]);

$statusCode = $response->getStatusCode();        // 200
$body = json_decode($response->getBody(), true);  // 解析 JSON

// POST 请求(发送 JSON)
$response = $client->post('/users', [
    'json' => [
        'name'  => '张三',
        'email' => 'zhangsan@example.com',
    ],
]);

// POST 表单数据
$response = $client->post('/login', [
    'form_params' => [
        'username' => 'admin',
        'password' => 'secret',
    ],
]);

Exception Handling

<?php
try {
    $response = $client->get('/users/999');
} catch (RequestException $e) {
    if ($e->hasResponse()) {
        $error = json_decode($e->getResponse()->getBody(), true);
        echo "错误: " . $error['message'];
    } else {
        echo "请求失败: " . $e->getMessage();
    }
}

Async Requests

<?php
use GuzzleHttp\Promise\Utils;

$promises = [
    'users'    => $client->getAsync('/users'),
    'products' => $client->getAsync('/products'),
    'orders'   => $client->getAsync('/orders'),
];

// 并发执行所有请求
$results = Utils::unwrap($promises);

$users    = json_decode($results['users']->getBody(), true);
$products = json_decode($results['products']->getBody(), true);
$orders   = json_decode($results['orders']->getBody(), true);
💡 Tip: In Chapter 5 we used raw cURL to send requests. In real projects, Guzzle is recommended — the API is cleaner, with built-in retry, connection pooling, async, and other advanced features.

3 Monolog — Logging Library

Monolog is the standard logging library for PHP (following the PSR-3 interface), supporting output to files, databases, Slack, email, and many other targets. Frameworks like Laravel and Symfony include Monolog by default.

composer require monolog/monolog

Basic Usage

<?php
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\LineFormatter;

// 创建日志通道
$log = new Logger('app');

// Handler 1:所有日志写入文件
$log->pushHandler(new StreamHandler(__DIR__ . '/logs/app.log', Logger::DEBUG));

// Handler 2:错误日志单独归档,按天轮转,保留 30 天
$errorHandler = new RotatingFileHandler(
    __DIR__ . '/logs/error.log',
    30,              // 保留天数
    Logger::ERROR    // 最低记录级别
);
$log->pushHandler($errorHandler);

// 写入日志
$log->debug('调试信息', ['module' => 'auth']);
$log->info('用户登录成功', ['user_id' => 42]);
$log->warning('缓存命中率低', ['rate' => 0.35]);
$log->error('数据库连接失败', ['host' => 'db.example.com']);
$log->critical('支付网关无响应');

Log Levels (Low to High)

Level Usage
DEBUGDetailed debug information
INFOKey business events (user login, order creation)
NOTICENormal but noteworthy events
WARNINGAnomalies that don't affect operation
ERRORRuntime errors that need fixing
CRITICALCritical component unavailable
ALERTRequires immediate action
EMERGENCYSystem is unusable

Custom Formatting

<?php
$formatter = new LineFormatter(
    "[%datetime%] %channel%.%level_name%: %message% %context%\n",
    'Y-m-d H:i:s'
);

$handler = new StreamHandler(__DIR__ . '/logs/app.log', Logger::DEBUG);
$handler->setFormatter($formatter);

$log = new Logger('app');
$log->pushHandler($handler);
// 输出: [2026-03-31 10:30:00] app.INFO: 用户登录成功 {"user_id":42}

4 Carbon — Date & Time

Carbon is an enhanced wrapper around PHP's DateTime, providing an extremely fluent API for date and time handling. It comes integrated by default in Laravel.

composer require nesbot/carbon
🔄 Cross-Language Comparison: Carbon is to PHP what dayjs / moment.js is to JavaScript, and pendulum / arrow is to Python. PHP's native DateTime has limited functionality, and Carbon fills the gap.

Creating & Formatting

<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

// 创建实例
$now = Carbon::now();                         // 当前时间
$today = Carbon::today();                     // 今天 00:00:00
$date = Carbon::create(2026, 3, 31, 10, 30);  // 指定时间
$parsed = Carbon::parse('2026-03-31 10:30:00');

// 格式化输出
echo $now->format('Y-m-d H:i:s');   // 2026-03-31 10:30:00
echo $now->toDateString();           // 2026-03-31
echo $now->toDateTimeString();       // 2026-03-31 10:30:00
echo $now->isoFormat('YYYY年MM月DD日'); // 2026年03月31日

Date Arithmetic

<?php
$now = Carbon::now();

// 加减运算
$tomorrow = $now->copy()->addDay();
$nextWeek = $now->copy()->addWeek();
$lastMonth = $now->copy()->subMonth();
$future = $now->copy()->addDays(45)->addHours(3);

// 链式操作
$deadline = Carbon::parse('2026-12-31')
    ->subDays(7)
    ->startOfDay();   // 2026-12-24 00:00:00

Comparison & Differences

<?php
$start = Carbon::parse('2026-01-01');
$end = Carbon::parse('2026-03-31');

echo $start->diffInDays($end);        // 89
echo $start->diffInMonths($end);      // 2
echo $start->diffForHumans($end);     // "2个月前"

// 布尔判断
$date = Carbon::parse('2026-03-31');
$date->isWeekday();     // true
$date->isToday();       // true (假设今天是 3/31)
$date->isFuture();      // false
$date->isPast();        // false
$date->isLeapYear();    // false

// 比较
$a = Carbon::parse('2026-01-01');
$b = Carbon::parse('2026-06-01');
$a->lt($b);   // true (less than)
$a->gte($b);  // false (greater than or equal)

Timezone Handling

<?php
$beijing = Carbon::now('Asia/Shanghai');
$newYork = $beijing->copy()->setTimezone('America/New_York');

echo $beijing->format('Y-m-d H:i');   // 2026-03-31 22:30
echo $newYork->format('Y-m-d H:i');   // 2026-03-31 10:30
⚠️ Note: Carbon's add/sub methods modify the original object. If you need to preserve the original date, use copy() first, or use the immutable version CarbonImmutable.

5 PHPUnit — Unit Testing

PHPUnit is the de facto standard testing framework for PHP, supporting unit testing, mocking, code coverage, and more.

# 安装为开发依赖
composer require --dev phpunit/phpunit

# 运行测试
./vendor/bin/phpunit

phpunit.xml Configuration

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
         colors="true"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Unit">
            <directory>tests</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory>src</directory>
        </include>
    </source>
</phpunit>

Writing Test Classes

<?php
// tests/CalculatorTest.php
namespace Tests;

use PHPUnit\Framework\TestCase;
use App\Calculator;

class CalculatorTest extends TestCase
{
    private Calculator $calc;

    // 每个测试方法执行前调用
    protected function setUp(): void
    {
        $this->calc = new Calculator();
    }

    // 每个测试方法执行后调用
    protected function tearDown(): void
    {
        // 清理资源(如果需要)
    }

    public function testAdd(): void
    {
        $this->assertEquals(5, $this->calc->add(2, 3));
        $this->assertEquals(0, $this->calc->add(-1, 1));
    }

    public function testDivide(): void
    {
        $this->assertEquals(2.5, $this->calc->divide(5, 2));
    }

    public function testDivideByZeroThrowsException(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('除数不能为零');
        $this->calc->divide(10, 0);
    }
}

Common Assertions

<?php
// 相等性
$this->assertEquals('hello', $result);     // 值相等(== 宽松比较)
$this->assertSame('hello', $result);       // 类型+值完全相同(===)

// 布尔
$this->assertTrue($user->isActive());
$this->assertFalse($user->isBanned());
$this->assertNull($user->deletedAt());

// 数组
$this->assertCount(3, $items);
$this->assertContains('admin', $roles);
$this->assertArrayHasKey('email', $data);
$this->assertEmpty($errors);

// 类型与实例
$this->assertInstanceOf(User::class, $result);
$this->assertIsString($name);
$this->assertIsArray($list);

// 字符串
$this->assertStringContainsString('error', $message);
$this->assertMatchesRegularExpression('/^\d{4}-\d{2}/', $date);

Data Providers

<?php
use PHPUnit\Framework\Attributes\DataProvider;

class MathTest extends TestCase
{
    public static function additionProvider(): array
    {
        return [
            '正数相加'   => [3, 4, 7],
            '负数相加'   => [-1, -2, -3],
            '零加任意数' => [0, 5, 5],
            '大数相加'   => [PHP_INT_MAX, 0, PHP_INT_MAX],
        ];
    }

    #[DataProvider('additionProvider')]
    public function testAdd(int $a, int $b, int $expected): void
    {
        $this->assertSame($expected, $a + $b);
    }
}
# 运行特定测试文件
./vendor/bin/phpunit tests/CalculatorTest.php

# 运行特定方法
./vendor/bin/phpunit --filter testAdd

# 显示详细输出
./vendor/bin/phpunit --testdox

6 vlucas/phpdotenv — Environment Variables

phpdotenv loads environment variables from a .env file, separating sensitive configuration (database passwords, API keys) from code.

composer require vlucas/phpdotenv

.env File Example

# .env(不要提交到 Git!加入 .gitignore)
APP_NAME=MyApp
APP_ENV=production
APP_DEBUG=false

DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secret

REDIS_HOST=127.0.0.1
API_KEY=sk-xxxxxxxxxxxx

Loading & Usage

<?php
require 'vendor/autoload.php';

// 加载 .env 文件(通常在应用入口处执行一次)
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

// 必须存在的变量(缺少则抛异常)
$dotenv->required(['DB_HOST', 'DB_DATABASE', 'DB_USERNAME']);

// 读取环境变量(三种方式)
$dbHost = $_ENV['DB_HOST'];               // 推荐
$dbHost = $_SERVER['DB_HOST'];            // 也可以
$dbHost = getenv('DB_HOST');              // 传统方式

// 实际使用
$dsn = sprintf(
    'mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4',
    $_ENV['DB_HOST'],
    $_ENV['DB_PORT'],
    $_ENV['DB_DATABASE']
);

$pdo = new PDO($dsn, $_ENV['DB_USERNAME'], $_ENV['DB_PASSWORD']);

Best Practices

✅ Recommended

  • • Add .env to .gitignore
  • • Provide .env.example as a template
  • • Use required() to validate essential variables
  • • Use real environment variables in production instead of .env

❌ Avoid

  • • Committing .env to version control
  • • Hardcoding passwords or keys in code
  • • Calling load() multiple times outside the entry file
  • • Storing large text blocks in .env

7 Other Recommended Libraries

The PHP ecosystem has many high-quality libraries. Here are some frequently used in real projects:

🖥️

symfony/console

A framework for building CLI tools, supporting argument parsing, colored output, progress bars, and interactive prompts.

composer require symfony/console
📁

league/flysystem

Filesystem abstraction layer providing a unified API for local disk, S3, FTP, Alibaba Cloud OSS, and more.

composer require league/flysystem
🔑

ramsey/uuid

Generates RFC 4122 standard UUIDs (v4 random, v7 time-ordered), widely used for distributed IDs.

composer require ramsey/uuid

respect/validation

A fluent data validation library with chainable API for validating email, phone, length, range, and more.

composer require respect/validation
🔴

predis/predis

A pure PHP Redis client that connects to Redis without requiring any C extensions.

composer require predis/predis
🖼️

intervention/image

An image processing library supporting crop, resize, watermark, and format conversion with an elegant API.

composer require intervention/image

Quick Examples

<?php
// ramsey/uuid - 生成 UUID
use Ramsey\Uuid\Uuid;

$uuid = Uuid::uuid4()->toString();  // "550e8400-e29b-41d4-a716-446655440000"
$uuid7 = Uuid::uuid7()->toString(); // 时间排序的 UUID v7

// respect/validation - 数据验证
use Respect\Validation\Validator as v;

$valid = v::email()->validate('user@example.com');          // true
$valid = v::stringType()->length(2, 50)->validate('张三');   // true
$valid = v::numericVal()->between(1, 100)->validate(42);    // true

// predis/predis - Redis 操作
$redis = new Predis\Client(['host' => '127.0.0.1']);
$redis->set('user:1:name', '张三');
$redis->expire('user:1:name', 3600);
$name = $redis->get('user:1:name'); // "张三"

8 PHP Framework Overview

After mastering PHP fundamentals, choosing a framework can dramatically boost development efficiency. Here are the three most popular frameworks today:

Framework Type Features Use Cases Learning Curve
Laravel Full-stack Elegant syntax, Eloquent ORM, Blade templates, queues, broadcasting, rich ecosystem Web apps, SaaS, API backends ⭐⭐ Medium
Symfony Full-stack Enterprise-grade, component-based design, highly configurable, long-term support Large enterprise projects, complex business systems ⭐⭐⭐ Higher
Slim Micro-framework Lightweight, routing + middleware only, freedom to choose components REST APIs, microservices, small projects ⭐ Simple

Quick Project Creation

# Laravel
composer create-project laravel/laravel my-app
cd my-app && php artisan serve   # 启动开发服务器 http://localhost:8000

# Symfony
composer create-project symfony/skeleton my-app
cd my-app && symfony server:start

# Slim
composer require slim/slim slim/psr7

Slim Micro-Framework Example

<?php
// public/index.php
require __DIR__ . '/../vendor/autoload.php';

use Slim\Factory\AppFactory;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

$app = AppFactory::create();
$app->addErrorMiddleware(true, true, true);

$app->get('/', function (Request $request, Response $response) {
    $response->getBody()->write(json_encode(['message' => '你好,PHP!']));
    return $response->withHeader('Content-Type', 'application/json');
});

$app->get('/users/{id}', function (Request $request, Response $response, array $args) {
    $userId = $args['id'];
    $response->getBody()->write(json_encode(['id' => $userId]));
    return $response->withHeader('Content-Type', 'application/json');
});

$app->run();
🎯 Framework Selection Guide:
  • • Beginners or building APIs → Slim (lightweight, fewer concepts, quick start)
  • • Rapid web app development → Laravel (richest ecosystem, most active community, excellent docs)
  • • Large enterprise projects → Symfony (rigorous architecture, suitable for complex business)
  • • After completing this tutorial, we recommend starting with Laravel or Slim for hands-on practice

9 Chapter Summary

📦 Composer

  • require to install, install to deploy
  • ^ / ~ semantic version constraints
  • • PSR-4 autoloading maps classes to files

🌐 Guzzle

  • • GET/POST requests, JSON handling
  • • Async concurrent requests
  • • Best alternative to raw cURL

📋 Monolog

  • • PSR-3 standard logging interface
  • • Handlers control output targets
  • • 8 levels from DEBUG to EMERGENCY

📅 Carbon

  • • Enhanced DateTime wrapper
  • • Fluent chainable date operations
  • copy() to avoid modifying the original

🧪 PHPUnit

  • TestCase + assert* methods
  • • Data Provider parameterized tests
  • setUp/tearDown manage test lifecycle

🔒 phpdotenv

  • • Load env variables from .env files
  • • Separate sensitive config from code
  • • Don't commit .env; do commit .env.example

🎉 Congratulations on completing the PHP Quick Tutorial!

You've mastered the core knowledge of PHP 8.4 — from environment setup, basic syntax, functions & OOP, database operations, network programming, file processing, to the Composer ecosystem and essential tool libraries.

Next Steps:

  1. Build a complete REST API project using Slim or Laravel
  2. Write PHPUnit tests for your project and adopt test-driven habits
  3. Read the PHP-FIG PSR Standards to learn community best practices
  4. Explore more open-source packages on Packagist