Классы в PHP

24 февраля 2016
Документация https://www.php.net/manual/ru/language.oop5.php

Область видимости

  • Если область видимости не задана, то подразумевается область public;
  • В наследуемых методах, свойствах и константах действуют правила:
    public нельзя изменять (возникнет ошибка).
    protected можно поменять на public.
    private можно изменить на public и protected, подразумевая, что создаётся новая сущность;

Ключевое слово static

1) Используется для создания свойств и методов, к которым можно обращаться без создания объекта (то есть можно обращаться через имя класса)
class st
{
	public static $def_str = 'пусто'; //статическое свойство

	public static function write_str($str='') //статический метод
	{
		if($str=='') $str = self::$def_str;
		echo $str;
	}
}

//Вызов статического метода 'write_str'

st::write_str(); //Выведет 'пусто'

st::$def_str = 'ничего'; //Изменяем статическое свойство

st::write_str(); //Выведет 'ничего'

st::write_str('Тест'); //Выведет 'Тест'

2) Дополнительно static используется для вызова метода в режиме позднего статичного связывания. Это нужно, когда в классе-родителе вызывается метод или свойство, которое переопределено в дочернем классе. Но вызов идёт в родителе, а значит вызывается родительский метод. Позднее статическое связывание позволяет вызывать из родительского класса переопределённые в наследниках методы.
class sh_1
{
	public function write_hello()
	{
		//Обычный вызов метода класса
		echo self::get_hello();
	}

	public function write_hello_v2()
	{
		//Вызов метода класса, к которому относится вызывающий объект.
		//Такой вызов (отсылка к классу) называется "Поздним статическим связыванием"
		echo static::get_hello();
	}

	protected function get_hello()
	{
		return 'Привет';
	}
}

class sh_2 extends sh_1
{
	protected function get_hello()
	{
		return 'Здравствуйте';
	}
}

$a = new sh_2();
$a->write_hello(); //Выведет 'Привет'
$a->write_hello_v2(); //Выведет 'Здравствуйте'

Абстрактные классы

Используются для создания оболочек классов, в которых методы могут не содержать определения. Такие методы и класс помечаются словом abstract, при этом класс не может иметь объекты. В классах, наследующих абстрактный класс, все абстрактные методы должны быть определены.
//Объявляем абстрактный класс
abstract class ab_1
{
	abstract public function write_hello($str=''); //объявление абстрактного метода
	public function get_hello()
	{
		return 'Привет';
	}
}

//Объявляем простой класс, происходящий от абстрактного
class ab_2 extends ab_1
{
	//Количество аргументов должно совпадать с абстрактным методом,
	//иначе возникнет ошибка ' Declaration ... must be compatible with ...
	function write_hello($str='')
	{
		if($str!='') echo $str;
		else echo $this->get_hello();
	}
}

$a = new ab_2;
$a->write_hello(); //выведет 'Привет'
$a->write_hello('Здравствуйте'); //выведет 'Здравствуйте'

Запрет наследования класса и запрет переопределения метода

1) Для запрета наследования класса перед объявлением класса указывается ключевое слово final
//Объявление финального класса
final class ff_1
{
	public static function write_hello()
	{
		echo 'Привет';
	}
}

//Объявляем новый класс, наследуемый от финального
class ff_2 extends ff_1
{}

//Скрипт выведет ошибку 'Class ff_2 may not inherit from final class (ff_1)'
//Перевод ошибки: 'Класс ff_2 не может наследоваться от финального класса ff_1'

2) Для запрета переопределения метода перед объявлением метода указывается ключевое слово final
class fs_1
{
	//Метод с ключевым словом 'final'
	final public static function m()
	{
		echo 'Тест 1';
	}
}

class fs_2 extends fs_1
{
	//Пробуем переопределить финальный метод
	public static function m()
	{
		echo 'Тест 2';
	}
}

//Скрипт выведет ошибку 'Cannot override final method fs_1::m()' (Не могу переопределить финальный метод)

Копирование объекта

Типовое присвоение объекта новой переменной сделает эту переменную ссылкой на объект. Для того, что бы переменная стала новым объектом нужно использовать оператор clone. Данный оператор создаёт новый объект, клонируя текущий и на последнем шаге вызывает магический метод __clone(), если тот объявлен в классе.
class zz
{
	public $word = 'Тест';

	//Объявляем специальный метод, который будет вызван на последнем шаге копирования объекта
	public function __clone()
	{
    	echo 'Объект скопирован';
	}
}

$a = new zz();
$a->word = 'Клубника';

//Копируем объект в новую переменную.
//Так же сработает метод __clone(), который выведет текст 'Объект скопирован'
$b = clone $a;

//Изменяем свойство, что бы проверить не стала ли переменная ссылкой
$a->word = 'Груша';

echo $b->word; //Выведет 'Клубника'

Проверка происхождения объекта

Для проверки происхождения объекта от заданного класса используется оператор instanceof
//Класс №1
class tt_base
{
	public $prop = 'Значение';
}

//Класс №2, происходящий от класса №1
class tt_child extends tt_base
{}

//Класс №3
class aa
{
	public $prop = 'Значение';
}

//Создаём объёкт
$a = new tt_child();

//Проверяем происхождение объекта
echo (int)($a instanceof tt_base); //выведет '1'
echo (int)($a instanceof tt_child); //выведет '1'
echo (int)($a instanceof aa); //выведет '0'

Работа с необъявленными и недоступными свойствами

Это ещё называется перегрузкой — возможность отлавливать и работать со свойствами, которые не были объявлены на этапе определения класса, а так же те, которые не доступны в запрашиваемой области видимости.
Документация https://www.php.net/manual/ru/language.oop5.overloading.php

Для работы с перегрузкой PHP предоставляет классам следующие методы:
  • public __set(string $name, mixed $value):void — запись данных в недоступное или несуществующее свойство;
  • public __get(string $name):mixed — получение значения недоступного или несуществующего свойства;
  • public __isset(string $name):bool — заменяет функцию isset при вызове её на недоступном или несуществующем свойстве;
  • public __unset(string $name):void — заменяет функцию unset при вызове её на недоступном или несуществующем свойстве.

Пример перегрузки свойств:
class user
{
	//Массив, в который будут складироваться все свойства
	protected $props = array();

	//Обработки записи в несуществующее/недоступное свойство
	public function __set($prop_code, $prop_val)
	{
		$prop_code = mb_strtolower($prop_code, 'utf-8');
		$this->props[$prop_code] = $prop_val;
	}

	//Обработка получения значения из несуществующего/недоступного свойства
	public function __get($prop_code)
	{
		$prop_code = mb_strtolower($prop_code, 'utf-8');
		return $this->props[$prop_code];
	}

	//Обработка проверки существования значения из несуществующего/недоступного свойства через функцию isset
	public function __isset($prop_code)
	{
		$prop_code = mb_strtolower($prop_code, 'utf-8');
		return isset($this->props[$prop_code]);
	}

	//Обработка удаления значения из несуществующего/недоступного свойства через функцию unset
	public function __unset($prop_code)
	{
		$prop_code = mb_strtolower($prop_code, 'utf-8');
		unset($this->props[$prop_code]);
	}

	//Магический метод преобразования объекта в строку
	public function __toString()
	{
		return '<pre>'.print_r($this->props, true).'</pre>';
	}
}

$user = new user();
$user->name = 'Владимир';
$user->class = '3';
$user->age = '9';
$user->note = 'Ходит в музыкальную школу';

//Выведет 'Заметка существует'
if(isset($user->note)) echo 'Заметка существует';
else if(isset($user->note)==false) echo 'Заметка НЕ существует';

//Удаление свойства (для теста)
unset($user->note);

//Выведет 'Заметка НЕ существует'
echo '<br>';
if(isset($user->note)) echo 'Заметка существует';
else if(isset($user->note)==false) echo 'Заметка НЕ существует';

/*
Выведет:
Array
(
    [name] => Владимир
    [class] => 3
    [age] => 9
)
*/
echo $user;

Работа с необъявленными и недоступными методами

Она же перегрузка — возможность отлавливать и работать с методами, которые не были объявлены на этапе определения класса или которые не доступны в запрашиваемой области видимости.
Документация https://www.php.net/manual/ru/language.oop5.overloading.php

Для работы с перегрузкой методов PHP предоставляет классам следующие магические методы:
  • public __call(string $name, array $arguments):mixed — вызов недоступного или несуществующего метода в контексте объекта;
  • public static __callStatic(string $name, array $arguments):mixed — вызов недоступного или несуществующего метода в контексте класса (в статическом контексте).

Пример перегрузки методов:
class user
{
	//Массив, хранящий сведения о пользователе
	private $info = array();

	//Метод для записи параметра
	public function set_param($prop_code, $prop_val)
	{
		$prop_code = mb_strtolower($prop_code, 'utf-8');
		$this->info[$prop_code] = $prop_val;
	}

	//Обработка вызова несуществующих/недоступных методов в контексте "Объект".
	//Если метод начинается с фразы "get_", то по оставшейся части названия метода (он же код записи) мы
	//получаем и возвращает конкретное сведение о пользователе
	public function __call($name, $args)
	{
		if(mb_strpos($name, 'get_', 0, 'utf-8')===0)
		{
        	$i_code = mb_substr($name, 4, null, 'utf-8'); //Вырезаем часть строки, содержащей название параметра
        	$i_code = mb_strtolower($i_code, 'utf-8');
        	return $this->info[$i_code];
		}
		else
		{
			echo 'Ошибка. Метод '.$name.' не объявлен в классе '.__CLASS__;
		}
	}

	//Обработка вызова несуществующих/недоступных методов в контексте "Класс"
	public static function __callStatic($name, $args)
	{
		if(mb_strpos($name, 'get_', 0, 'utf-8')===0)
		{
			echo 'Ошибка. Для работы с методом '.$name.' класса '.__CLASS__.' вы должны использовать объект';
		}
		else
		{
			echo 'Ошибка. Метод '.$name.' не объявлен в классе '.__CLASS__;
		}
	}
}

//Создаём объект "Пользователь" и наполняем его сведениями
$a = new user();
$a->set_param('name', 'Владимир');
$a->set_param('class', '3');
$a->set_param('age', '9');
$a->set_param('note', 'Ходит в музыкальную школу');

echo '<br>';
echo $a->return_name(); //Выведет 'Ошибка. Метод return_name не объявлен в классе user'

echo '<br>';
echo $a->get_name(); //Выведет 'Владимир'

echo '<br>';
var_dump($a->get_email()); //Выведет 'NULL'

echo '<br>';
echo $a->get_note(); //Выведет 'Ходит в музыкальную школу'

echo '<br>';
echo user::get_name(); //Выведет 'Ошибка. Для работы с методом get_name класса user вы должны использовать объект'

Преобразование объекта в строку

Магический метод public __toString():string позволяет задать строку, в которую должен преобразоваться объект при переходе в строчный тип
class user
{
	public $name; 		//Имя
	public $surname;	//Фамилия

	//Объявляем магическое свойство, которое будет использовано при преобразовании объекта в строку
	public function __toString()
	{
		$ar = array();
		if($this->name!='') $ar[] = $this->name;
		if($this->surname!='') $ar[] = $this->surname;

		return implode(' ', $ar);
	}
}

$u = new user();
$u->name = 'Мария';
$u->surname = 'Тугушева';

echo $u; //Выведет 'Мария Тугушева'

Магические методы

Магические методы используются для выполнения специальных обработок. Магические методы начинаются с __ (два нижних подчёркивания) и должны объявляться как public.
Документация https://www.php.net/manual/ru/language.oop5.magic.php

Список методов:
  • __construct([mixed $args [, $... ]]):void — конструктор, вызывается при создании объекта. При переопределении конструктора в наследнике, нужно вызывать родительский конструктор через parent::__construct();
  • __destruct(void):void — деструктор, вызывается при удалении объекта. При переопределении деструктора в наследнике, нужно вызывать родительский деструктор через parent::__destruct();
  • __set(string $name, mixed $value):void — запись данных в недоступное или несуществующее свойство;
  • __get(string $name):mixed — получение значения недоступного или несуществующего свойства;
  • __isset(string $name):bool — заменяет функцию isset при вызове её на недоступном или несуществующем свойстве;
  • __unset(string $name):void — заменяет функцию unset при вызове её на недоступном или несуществующем свойстве;
  • __call(string $name, array $arguments):mixed — вызов недоступного или несуществующего метода в контексте объекта;
  • __callStatic(string $name, array $arguments):mixed — вызов недоступного или несуществующего метода в контексте класса (в статическом контексте). Метод должен объявляться с ключевым словом static;
  • __sleep(void):array — вызывается перед сериализацией (функция serialize) и должен возвращать массив с именами свойств, которые попадут в сериализацию. Если указан пустой массив, то ни одно свойство объекта не попадёт в сериализацию;
  • __wakeup(void):void — вызывается после завершения десериализации (функция unserialize);
  • __toString(void):string — возвращает строку, в которую должен преобразоваться объект при переходе в строчный тип;
  • __invoke([ $... ]):mixed — выполняется при вызове объекта как функция;
  • __set_state(array $properties):object — вызывается при применении к объекту функции var_export (функция создаёт сточное представление переменной). В аргументах метода передаётся массив, содержащий свойства объекта в формате имя свойства => значение. Метод должен возвращать объект, у которого заполнены все свойства, подлежащие экспорту. Метод должен объявляться с ключевым словом static;
  • __clone(void):void — вызывается после копирования объекта через оператор clone;
  • _debugInfo(void):array — вызывается при применении к объекту функции var_dump (функция для печати содержимого переменной). Метод возвращает массив, содержащий свойства объекта в формате имя свойства => значение. Если метод объявлен, но возвращает пустой массив, то ни одно свойство не попадёт в var_dump;