Изучаем PHP 5: от базовых классов до продвинутых средств
Основные возможности PHP 5
PHP 5 стал важным этапом развития языка, внедрив современную объектно-ориентированную модель, механизм исключений, улучшенную работу с базами данных через PDO и другие новшества. В этой статье рассматриваются ключевые возможности PHP 5 с примерами кода.
Как создать и использовать классы в PHP 5?
Основой ООП в PHP 5 является класс. Класс объявляется с помощью ключевого слова class. Свойства и методы могут иметь модификаторы доступа public, protected и private. Конструктор определяется методом __construct.
<?php
class User {
public $name;
private $password;
public function __construct($name, $password) {
$this->name = $name;
$this->password = password_hash($password, PASSWORD_DEFAULT);
}
public function verifyPassword($password) {
return password_verify($password, $this->password);
}
}
$user = new User('Иван', 'secret123');
echo $user->name; // Иван
var_dump($user->verifyPassword('wrong')); // false
?>Иван bool(false)
Типичные ошибки:
- Забывание указать модификатор доступа – по умолчанию public.
- Обращение к приватному свойству извне класса вызывает фатальную ошибку.
- Использование $this в статическом методе.
Решение: всегда явно объявлять видимость, для статических методов использовать self:: или static::.
Как реализовать наследование и интерфейсы?
Наследование позволяет создавать иерархию классов. Ключевое слово extends. Интерфейсы задают контракты с помощью interface и implements.
<?php
interface Logger {
public function log($message);
}
class FileLogger implements Logger {
public function log($message) {
file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
}
}
class DatabaseLogger extends FileLogger {
public function log($message) {
// запись в БД
echo 'DB: ' . $message . PHP_EOL;
}
}
$logger = new DatabaseLogger();
$logger->log('Test'); // DB: Test
?>DB: Test
При наследовании часто возникает путаница с вызовом родительского конструктора – необходимо явно вызывать parent::__construct().
Как обрабатывать ошибки с помощью исключений?
В PHP 5 появилась поддержка исключений через try, catch, throw. Исключения позволяют централизованно обрабатывать ошибки.
<?php
function divide($a, $b) {
if ($b == 0) {
throw new InvalidArgumentException('Деление на ноль');
}
return $a / $b;
}
try {
echo 'Результат: ' . divide(10, 2) . PHP_EOL;
echo divide(10, 0);
} catch (InvalidArgumentException $e) {
echo 'Ошибка: ' . $e->getMessage() . PHP_EOL;
}
?>Результат: 5 Ошибка: Деление на ноль
Ошибка: catch должен перехватывать конкретный класс исключения, иначе необработанные исключения приводят к фатальной ошибке. Рекомендуется добавлять блок catch (Exception $e) для всех остальных.
Как безопасно работать с базами данных через PDO?
PDO (PHP Data Objects) предоставляет единый интерфейс для работы с разными СУБД и защищает от SQL-инъекций через подготовленные запросы.
<?php
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8';
$user = 'root';
$password = '';
try {
$pdo = new PDO($dsn, $user, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => 'user@example.com']);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($user);
} catch (PDOException $e) {
echo 'Ошибка подключения: ' . $e->getMessage();
}
?>Array ( [id] => 1 [name] => ... )
Частая ошибка – использование PDO::query() без экранирования параметров. Всегда использовать подготовленные запросы для пользовательского ввода.
Как перегрузить доступ к свойствам и методам через магические методы?
Магические методы __get, __set, __call позволяют обрабатывать обращения к несуществующим свойствам/методам.
<?php
class Config {
private $data = [];
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __get($name) {
return $this->data[$name] ?? null;
}
public function __call($name, $arguments) {
if ($name === 'get') {
return $this->data[$arguments[0]] ?? null;
}
}
}
$cfg = new Config();
$cfg->host = 'localhost';
echo $cfg->host . PHP_EOL;
echo $cfg->get('host');
?>localhost localhost
Магические методы работают только для несуществующих свойств/методов. Если свойство объявлено, но private, то __get не срабатывает. Не следует злоупотреблять магией – она замедляет выполнение и усложняет отладку.
Как создать анонимную функцию (замыкание) в PHP 5.3+?
Начиная с PHP 5.3, появились замыкания (closures). Они могут захватывать переменные из внешней области видимости с помощью ключевого слова use.
<?php
$factor = 2;
$multiply = function($x) use ($factor) {
return $x * $factor;
};
echo $multiply(5) . PHP_EOL;
// Замыкание как callback для array_map
$numbers = [1, 2, 3];
$result = array_map($multiply, $numbers);
print_r($result);
?>10 Array ( [0] => 2 [1] => 4 [2] => 6 )
Ошибка: забыть указать use – тогда внешняя переменная недоступна внутри замыкания. Также замыкания нельзя сериализовать.
Расширенные примеры использования PHP 5
Здесь представлены более сложные и неочевидные возможности PHP 5: трейты, позднее статическое связывание, Reflection API, итераторы SPL.
Пример 1: Трейты (PHP 5.4+)
<?php
trait LoggerTrait {
public function log($msg) {
echo '[LOG] ' . $msg . PHP_EOL;
}
}
class Application {
use LoggerTrait;
}
$app = new Application();
$app->log('Запуск приложения');
?>[LOG] Запуск приложения
Пример 2: Позднее статическое связывание (Late Static Binding)
<?php
class Base {
public static function who() {
echo __CLASS__ . PHP_EOL;
}
public static function test() {
static::who(); // позднее статическое связывание
}
}
class Child extends Base {
public static function who() {
echo __CLASS__ . PHP_EOL;
}
}
Child::test(); // Child, а не Base
?>Child
Пример 3: Reflection API – анализ классов
<?php
class MyClass {
public $publicProp;
protected $protectedProp;
private $privateProp;
public function myMethod($arg) {}
}
$ref = new ReflectionClass('MyClass');
$properties = $ref->getProperties();
foreach ($properties as $prop) {
echo $prop->getName() . ' : ' . $prop->getModifiers() . PHP_EOL;
}
?>publicProp : 256 protectedProp : 512 privateProp : 1024
Пример 4: Итераторы SPL – DirectoryIterator
<?php
$dir = new DirectoryIterator('.');
foreach ($dir as $file) {
if ($file->isFile()) {
echo $file->getFilename() . ' (' . $file->getSize() . ' байт)' . PHP_EOL;
}
}
?>index.php (1234 байт) style.css (567 байт) ...
Пример 5: Использование ключевого слова final для методов и классов
<?php
class Base {
final public function cannotOverride() {
echo 'Финализированный метод' . PHP_EOL;
}
}
class Child extends Base {
// Ошибка: нельзя переопределить final метод
// public function cannotOverride() { }
}
?>Fatal error: Cannot override final method Base::cannotOverride()