Сравнение объектов в ООП на PHP
Сейчас мы с вами научимся сравнивать объекты
с помощью операторов ==
и ===
.
Вы должны уже знать, что для примитивов (то
есть не объектов) оператор ==
сравнивает
данные по значению без учета типа, а оператор
===
- учитывая тип:
<?php
var_dump(3 == 3); // выведет true
var_dump(3 == '3'); // выведет true
var_dump(3 === 3); // выведет true
var_dump(3 === '3'); // выведет false
?>
Давайте теперь посмотрим, как работает сравнение объектов.
При использовании оператора ==
для
сравнения двух объектов выполняется сравнение
свойств объектов: два объекта равны, если
они имеют одинаковые свойства и их значения
(значения свойств сравниваются через ==)
и являются экземплярами одного и того же класса.
При сравнении через ===
, переменные,
содержащие объекты, считаются равными только
тогда, когда они ссылаются на один и тот
же экземпляр одного и того же класса.
Давайте посмотрим на примере. Пусть у нас
дан вот такой класс User
:
<?php
class User
{
private $name;
private $age;
public function __construct($name, $age)
{
$this->name = $name;
$this->age = $age;
}
public function getName()
{
return $this->name;
}
public function getAge()
{
return $this->age;
}
}
?>
Создадим два объекта нашего класса с одинаковыми значениями свойств и сравним созданные объекты:
<?php
$user1 = new User('john', 30);
$user2 = new User('john', 30);
var_dump($user1 == $user2); // выведет true
?>
Пусть теперь значения свойств одинаковые, но у них разный тип:
<?php
$user1 = new User('john', 30); // возраст - число
$user2 = new User('john', '30'); // возраст - строка
var_dump($user1 == $user2); // выведет true
?>
Пусть значения свойств разные:
<?php
$user1 = new User('john', 30);
$user2 = new User('john', 25);
var_dump($user1 == $user2); // выведет false
?>
Давайте теперь сравним два наших объекта
через ===
:
<?php
$user1 = new User('john', 30);
$user2 = new User('john', 30);
var_dump($user1 === $user2); // выведет false
?>
Чтобы две переменных с объектами действительно
были равными при сравнении через ===
,
они должны указывать на один и тот же объект.
Давайте сделаем, чтобы это было так, и сравним
переменные:
<?php
$user1 = new User('john', 30);
$user2 = $user1; // передача объекта по ссылке
var_dump($user1 === $user2); // выведет true
?>
Сделайте функцию compare
, которая
параметром будет принимать два объекта и
возвращать true
, если они имеют одинаковые
свойства и значения являются экземплярами
одного и того же класса, и false
,
если это не так.
Сделайте функцию compare
, которая
параметром будет принимать два объекта и
возвращать true
, если переданные переменные
ссылаются на один и тот же объект, и false
,
если на разные.
Сделайте функцию compare
, которая
параметром будет принимать два объекта и
сравнивать их.
Функция должна возвращать 1
, если
переданные переменные ссылаются на один и
тот же объект.
Функция должна возвращать 0
, если
объекты разные, но одного и того же класса
и с теми же свойствами и их значениями.
Функция должна возвращать -1
в противном
случае.
Применение
Давайте рассмотрим применение изученной теории.
Пусть у нас дан вот такой класс Employee
:
<?php
class Employee
{
private $name;
private $salary;
public function __construct($name, $salary)
{
$this->name = $name;
$this->salary = $salary;
}
public function getName()
{
return $this->name;
}
public function getSalary()
{
return $this->salary;
}
}
?>
Пусть также дан такой класс EmployeesCollection
для хранения коллекции работников:
<?php
class EmployeesCollection
{
private $employees = []; // массив работников
// Добавляем нового работника:
public function add($newEmployee)
{
$this->employees[] = $newEmployee;
}
// Получаем всех работников в виде массива:
public function get()
{
return $this->employees;
}
}
?>
Давайте сделаем так, чтобы работник, который
уже есть в нашем наборе, не добавлялся еще
раз. Для этого сделаем вспомогательный метод
exists
, который будет принимать объект
с новым работником и проверять его наличие
в массиве $this->employees
:
<?php
private function exists($newEmployee)
{
foreach ($this->employees as $employee) {
if ($employee == $newEmployee) { // сравниваем через ==
return true;
}
}
return false;
}
?>
Давайте применим новый метод в нашем классе:
<?php
class EmployeesCollection
{
private $employees = [];
public function add($newEmployee)
{
// Если такого работника нет - то добавляем:
if (!$this->exists($newEmployee)) {
$this->employees[] = $newEmployee;
}
}
public function get()
{
return $this->employees;
}
private function exists($newEmployee)
{
foreach ($this->employees as $employee) {
if ($employee == $newEmployee) {
return true;
}
}
return false;
}
}
?>
Давайте проверим работу нашего класса:
<?php
$employeesCollection = new EmployeesCollection;
$employeesCollection->add(new Employee('john', 100));
$employeesCollection->add(new Employee('john', 100)); // второго такого же не добавит
var_dump($employeesCollection->get()); // убедимся в этом
?>
В общем-то, мы получили устраивающий нас код. Но давайте попробуем поиграться с ним: поменяем при сравнении двойное равно на тройное:
<?php
private function exists($newEmployee)
{
foreach ($this->products as $product) {
if ($product === $newEmployee) { // сравниваем через ===
return true;
}
}
return false;
}
?>
Теперь при попытке добавить нового работника с такими же значениями свойств он будет добавляться:
<?php
$employeesCollection = new EmployeesCollection;
$employeesCollection->add(new Employee('john', 100));
$employeesCollection->add(new Employee('john', 100)); // добавит
var_dump($employeesCollection->get());
?>
Но если попытаться добавить тот же объект, то добавления не произойдет:
<?php
$employeesCollection = new EmployeesCollection;
$employee = new Employee('john', 100);
$employeesCollection->add($employee);
$employeesCollection->add($employee); // не добавит, тк тот же объект
var_dump($employeesCollection->get());
?>
Скопируйте мой код класса Employee
,
затем самостоятельно реализуйте описанный
класс EmployeesCollection
, проверьте
его работу.
Функция in_array
На самом деле код метода exists
можно
заменить стандартной PHP функцией in_array
.
Нужно только знать, как она выполняет сравнение
- по двойному равно или по тройному.
Из документации следует, что эта функция
имеет третий необязательный параметр. Если
он не установлен или равен false
-
функция сравнивает по двойному равно, а равен
true
- то по тройному.
Давайте упростим код класса при условии сравнения объектов через двойное равно:
<?php
class EmployeesCollection
{
private $employees = [];
public function add($newEmployee)
{
if (!in_array($newEmployee, $this->employees, false)) {
$this->employees[] = $newEmployee;
}
}
public function get()
{
return $this->employees;
}
}
?>
А теперь при условии сравнения на тройное равно:
<?php
class EmployeesCollection
{
private $employees = [];
public function add($newEmployee)
{
// Эквивалентно методу exists с ===
if (!in_array($newEmployee, $this->employees, true)) {
$this->employees[] = $newEmployee;
}
}
public function get()
{
return $this->employees;
}
}
?>
Упростите ваш класс EmployeesCollection
с использованием функции in_array
,
проверьте его работу.