Сейчас мы с вами научимся сравнивать объекты
с помощью операторов == и ===.
Вы должны уже знать, что для примитивов (то
есть не объектов) оператор == сравнивает
данные по значению без учета типа, а оператор
=== - учитывая тип:
<?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,
проверьте его работу.