Поиск…


Вступление

Классы и объекты используются, чтобы сделать ваш код более эффективным и менее повторяющимся, группируя подобные задачи.

Класс используется для определения действий и структуры данных, используемых для создания объектов. Затем объекты создаются с использованием этой предопределенной структуры.

Синтаксис

  • class <ClassName> [ extends <ParentClassName> ] [ implements <Interface1> [, <Interface2>, ... ] { } // Объявление класса
  • interface <InterfaceName> [ extends <ParentInterface1> [, <ParentInterface2>, ...] ] { } // Объявление интерфейса
  • use <Trait1> [, <Trait2>, ...] ; // Использовать черты
  • [ public | protected | private ] [ static ] $<varName>; // Объявление атрибута
  • const <CONST_NAME>; // Декларация константы
  • [ public | protected | private ] [ static ] function <methodName>([args...]) { } // Объявление метода

замечания

Классы и компоненты интерфейса

Классы могут иметь свойства, константы и методы.

  • Свойства содержат переменные в объеме объекта. Они могут быть инициализированы при объявлении, но только если они содержат примитивное значение.
  • Константы должны быть инициализированы в декларации и могут содержать только примитивное значение. Константные значения фиксируются во время компиляции и не могут быть назначены во время выполнения.
  • Методы должны иметь тело, даже пустое, если метод не объявлен абстрактным.
class Foo {
    private $foo = 'foo'; // OK
    private $baz = array(); // OK
    private $bar = new Bar(); // Error!
}

Интерфейсы не могут иметь свойств, но могут иметь константы и методы.

  • Константы интерфейса должны быть инициализированы при объявлении и могут содержать только примитивное значение. Константные значения фиксируются во время компиляции и не могут быть назначены во время выполнения.
  • Методы интерфейса не имеют тела.
interface FooBar {
    const FOO_VALUE = 'bla';
    public function doAnything();
}

Интерфейсы

Вступление

Интерфейсы - это определения общедоступных классов API, которые должны реализовывать, чтобы удовлетворить интерфейс. Они работают как «контракты», указывая, что делает набор подклассов, но не то, как они это делают.

Определение интерфейса очень похоже на определение класса, изменение class ключевого слова для interface :

interface Foo {

}

Интерфейсы могут содержать методы и / или константы, но не атрибуты. Интерфейсные константы имеют те же ограничения, что и константы класса. Методы интерфейса неявно абстрактны:

interface Foo {
    const BAR = 'BAR';

    public function doSomething($param1, $param2);
}

Примечание. Интерфейсы не должны объявлять конструкторы или деструкторы, поскольку это детали реализации на уровне класса.

реализация

Любой класс , который должен реализовать интерфейс должен сделать это , используя implements ключевое слово. Для этого класс должен обеспечить реализацию для каждого метода, объявленного в интерфейсе, в соответствии с той же сигнатурой.

Один класс может одновременно реализовать более одного интерфейса.

interface Foo {
    public function doSomething($param1, $param2);
}

interface Bar {
    public function doAnotherThing($param1);
}


class Baz implements Foo, Bar {
    public function doSomething($param1, $param2) {
        // ...
    }

    public function doAnotherThing($param1) {
        // ...
    }
}

Когда абстрактные классы реализуют интерфейсы, им не нужно реализовывать все методы. Любой метод, не реализованный в базовом классе, должен быть реализован конкретным классом, который его расширяет:

abstract class AbstractBaz implements Foo, Bar {
    // Partial implementation of the required interface...
    public function doSomething($param1, $param2) {
        // ...
    }
}

class Baz extends AbstractBaz {
    public function doAnotherThing($param1) {
        // ...
    }
}

Обратите внимание, что реализация интерфейса является наследуемой характеристикой. При расширении класса, реализующего интерфейс, вам не нужно его повторять в конкретном классе, потому что он неявный.

Примечание. До PHP 5.3.9 класс не смог реализовать два интерфейса, которые указали метод с тем же именем, поскольку это вызовет неоднозначность. Более поздние версии PHP допускают это, если дублирующие методы имеют одну и ту же подпись [1] .

наследование

Подобно классам, можно установить отношения наследования между интерфейсами, используя одно и то же ключевое слово extends . Основное различие заключается в том, что для интерфейсов допускается множественное наследование:

interface Foo {

}

interface Bar {

}

interface Baz extends Foo, Bar {

}

Примеры

В приведенном ниже примере у нас есть простой пример интерфейса для транспортного средства. Транспортные средства могут двигаться вперед и назад.

interface VehicleInterface {
    public function forward();

    public function reverse();

    ...
}

class Bike implements VehicleInterface {
    public function forward() {
        $this->pedal();
    }

    public function reverse() {
        $this->backwardSteps();
    }

    protected function pedal() {
        ...
    }

    protected function backwardSteps() {
        ...
    }

    ...
}

class Car implements VehicleInterface {
    protected $gear = 'N';

    public function forward() {
        $this->setGear(1);
        $this->pushPedal();
    }

    public function reverse() {
        $this->setGear('R');
        $this->pushPedal();
    }

    protected function setGear($gear) {
        $this->gear = $gear;
    }

    protected function pushPedal() {
        ...
    }

    ...
}

Затем мы создаем два класса, которые реализуют интерфейс: Bike and Car. Велосипед и автомобиль внутри очень разные, но оба являются транспортными средствами и должны реализовывать те же общедоступные методы, которые предоставляет VehicleInterface.

Typehinting позволяет методам и функциям запрашивать интерфейсы. Предположим, что у нас есть класс гаража, который содержит автомобили всех видов.

class ParkingGarage {
    protected $vehicles = [];

    public function addVehicle(VehicleInterface $vehicle) {
        $this->vehicles[] = $vehicle;
    }
}

Потому что addVehicle требует $vehicle типа VehicleInterface - не конкретная реализация - мы можем вводить как велосипеды, так и автомобили, которые ParkingGarage может манипулировать и использовать.

Константы классов

Константы класса предоставляют механизм для хранения фиксированных значений в программе. То есть они предоставляют способ дать имя (и связанную проверку времени компиляции) до значения, такого как 3.14 или "Apple" . Константы класса могут быть определены только с помощью ключевого слова const - функция определения не может использоваться в этом контексте.

В качестве примера может быть удобно иметь сокращенное представление для значения π во всей программе. Класс со значениями const предоставляет простой способ для хранения таких значений.

class MathValues {
    const PI = M_PI;
    const PHI = 1.61803;
}

$area = MathValues::PI * $radius * $radius;

К константам класса можно получить доступ, используя оператор двойной толчки (так называемый оператор разрешения области) в классе, подобно статическим переменным. Однако, в отличие от статических переменных, константы класса имеют фиксированные значения во время компиляции и не могут быть переназначены (например, MathValues::PI = 7 приведет к фатальной ошибке).

Константы классов также полезны для определения вещей внутри класса, которые могут потребоваться позже изменить (но не изменяются достаточно часто, чтобы гарантировать хранение, скажем, базы данных). Мы можем ссылаться на это внутренне , используя self области действия resolutor (который работает в обоих инстансы и статических реализаций)

class Labor {
    /** How long, in hours, does it take to build the item? */
    const LABOR_UNITS = 0.26;
    /** How much are we paying employees per hour? */
    const LABOR_COST = 12.75;

    public function getLaborCost($number_units) {
         return (self::LABOR_UNITS * self::LABOR_COST) * $number_units;
    }
}

Константы класса могут содержать только скалярные значения в версиях <5.6

Начиная с PHP 5.6 мы можем использовать выражения с константами, то есть математические утверждения и строки с конкатенацией являются допустимыми константами

class Labor {
    /** How much are we paying employees per hour? Hourly wages * hours taken to make */
    const LABOR_COSTS = 12.75 * 0.26;

    public function getLaborCost($number_units) {
         return self::LABOR_COSTS * $number_units;
    }
}

Начиная с PHP 7.0, константы, объявленные с помощью define теперь могут содержать массивы.

define("BAZ", array('baz'));

Константы класса полезны не только для хранения математических понятий. Например, при приготовлении пирога может быть удобно иметь один класс Pie способный принимать различные виды фруктов.

class Pie {
    protected $fruit;

    public function __construct($fruit) {
        $this->fruit = $fruit;
    }
}

Затем мы можем использовать класс Pie

$pie = new Pie("strawberry");

Проблема, возникающая здесь, заключается в том, что при создании класса Pie никаких указаний относительно допустимых значений не предоставляется. Например, при создании пирога «мальковки» это может быть написано с ошибкой «boisenberry». Кроме того, мы не можем поддерживать сливочный пирог. Вместо этого было бы полезно иметь список приемлемых типов фруктов, которые уже были определены где-то, было бы целесообразно искать их. Скажите класс под названием Fruit :

class Fruit {
    const APPLE = "apple";
    const STRAWBERRY = "strawberry";
    const BOYSENBERRY = "boysenberry";
}

$pie = new Pie(Fruit::STRAWBERRY);

Перечисление допустимых значений в качестве констант класса дает ценный совет относительно приемлемых значений, которые принимает метод. Это также гарантирует, что орфографические ошибки не могут пройти мимо компилятора. В то время как new Pie('aple') и new Pie('apple') приемлемы для компилятора, new Pie(Fruit::APLE) приведет к ошибке компилятора.

Наконец, использование констант класса означает, что фактическое значение константы может быть изменено в одном месте, а любой код с использованием константы автоматически имеет последствия модификации.

Хотя наиболее распространенным методом доступа к константе класса является MyClass::CONSTANT_NAME , к нему также можно получить доступ:

echo MyClass::CONSTANT;

$classname = "MyClass";
echo $classname::CONSTANT; // As of PHP 5.3.0

Константы класса в PHP условно называются все в верхнем регистре с подчеркиваниями как разделители слов, хотя любое допустимое имя метки может использоваться как имя константы класса.

Начиная с PHP 7.1, теперь константы класса могут быть определены с различной видимостью из общедоступной области по умолчанию. Это означает, что теперь можно определить как защищенные, так и частные константы, чтобы предотвратить ненужные утечки констант класса в общедоступную область видимости (см. « Метод и видимость свойств» ). Например:

class Something {
    const PUBLIC_CONST_A = 1;
    public const PUBLIC_CONST_B = 2;
    protected const PROTECTED_CONST = 3;
    private const PRIVATE_CONST = 4;
}

определить vs константы класса

Хотя это действительная конструкция:

function bar() { return 2; };

define('BAR', bar());

Если вы попытаетесь сделать то же самое с константами класса, вы получите сообщение об ошибке:

function bar() { return 2; };

class Foo {
    const BAR = bar(); // Error: Constant expression contains invalid operations
}

Но вы можете сделать:

function bar() { return 2; };

define('BAR', bar());

class Foo {
    const BAR = BAR; // OK
}

Для получения дополнительной информации см. Константы в руководстве .

Использование :: class для извлечения имени класса

PHP 5.5 ввел ::class синтаксис , чтобы получить полное имя класса, принимая область пространства имен и use заявление во внимание.

namespace foo;
use bar\Bar;
echo json_encode(Bar::class); // "bar\\Bar"
echo json_encode(Foo::class); // "foo\\Foo"
echo json_encode(\Foo::class); // "Foo"

Вышеупомянутое работает, даже если классы даже не определены (т. Е. Этот фрагмент кода работает в одиночку).

Этот синтаксис полезен для функций, для которых требуется имя класса. Например, он может использоваться с class_exists для проверки класса. Ошибки не будут генерироваться независимо от возвращаемого значения в этом фрагменте:

class_exists(ThisClass\Will\NeverBe\Loaded::class, false);

Поздняя статическая привязка

В PHP 5.3+ и выше вы можете использовать позднюю статическую привязку для управления классом, из которого вызывается статическое свойство или метод. Он был добавлен для преодоления проблемы, присущей разрешателю self:: scope. Возьмите следующий код

class Horse {
    public static function whatToSay() {
         echo 'Neigh!';
    }

    public static function speak() {
         self::whatToSay();
    }
}

class MrEd extends Horse {
    public static function whatToSay() {
         echo 'Hello Wilbur!';
    }
}

Можно ожидать , что MrEd класс будет переопределить родительский whatToSay() функцию. Но когда мы запускаем это, мы получаем что-то неожиданное

Horse::speak(); // Neigh!
MrEd::speak(); // Neigh!

Проблема в том, что self::whatToSay(); может относиться только к классу Horse , то есть он не подчиняется MrEd . Если мы перейдем к разрешению static:: scope, у нас нет этой проблемы. Этот новый метод сообщает классу подчиняться экземпляру, вызывающему его. Таким образом, мы получаем наследство, которое мы ожидаем

class Horse {
    public static function whatToSay() {
         echo 'Neigh!';
    }

    public static function speak() {
         static::whatToSay(); // Late Static Binding
    }
}

Horse::speak(); // Neigh!
MrEd::speak(); // Hello Wilbur!

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

Абстрактный класс - это класс, который не может быть создан. Абстрактные классы могут определять абстрактные методы, которые являются методами без какого-либо тела, а только определение:

abstract class MyAbstractClass {
    abstract public function doSomething($a, $b);
}

Абстрактные классы должны быть расширены дочерним классом, который затем может обеспечить реализацию этих абстрактных методов.

Основная цель такого класса - предоставить своего рода шаблон, который позволяет дочерним классам наследовать, «заставляя» структуру, к которой она привязана. Давайте подробнее рассмотрим этот пример:

В этом примере мы реализуем интерфейс Worker . Сначала мы определяем интерфейс:

interface Worker {
    public function run();
}

Чтобы облегчить разработку дальнейших реализаций Worker, мы создадим абстрактный класс работника, который уже предоставляет метод run() из интерфейса, но указывает некоторые абстрактные методы, которые должны быть заполнены любым дочерним классом:

abstract class AbstractWorker implements Worker {
    protected $pdo;
    protected $logger;

    public function __construct(PDO $pdo, Logger $logger) {
        $this->pdo = $pdo;
        $this->logger = $logger;
    }

    public function run() {
        try {
            $this->setMemoryLimit($this->getMemoryLimit());
            $this->logger->log("Preparing main");
            $this->prepareMain();
            $this->logger->log("Executing main");
            $this->main();
        } catch (Throwable $e) {
            // Catch and rethrow all errors so they can be logged by the worker
            $this->logger->log("Worker failed with exception: {$e->getMessage()}");
            throw $e;
        }
    }

    private function setMemoryLimit($memoryLimit) {
        ini_set('memory_limit', $memoryLimit);
        $this->logger->log("Set memory limit to $memoryLimit");
    }

    abstract protected function getMemoryLimit();

    abstract protected function prepareMain();

    abstract protected function main();
}

Прежде всего, мы предоставили абстрактный метод getMemoryLimit() . Любой класс, простирающийся от AbstractWorker должен предоставить этот метод и вернуть свой предел памяти. Затем AbstractWorker устанавливает ограничение на память и записывает его в журнал.

Во-вторых, AbstractWorker вызывает prepareMain() и main() после регистрации, что они были вызваны.

Наконец, все эти вызовы методов были сгруппированы в блок try catch . Поэтому, если какой-либо из абстрактных методов, определяемых дочерним классом, генерирует исключение, мы поймаем это исключение, запишем его в журнал и перестроим. Это не позволяет всем дочерним классам реализовать это самостоятельно.

Теперь давайте определим дочерний класс, который простирается от AbstractWorker :

class TranscactionProcessorWorker extends AbstractWorker {
    private $transactions;

    protected function getMemoryLimit() {
        return "512M";
    }

    protected function prepareMain() {
        $stmt = $this->pdo->query("SELECT * FROM transactions WHERE processed = 0 LIMIT 500");
        $stmt->execute();
        $this->transactions = $stmt->fetchAll();
    }

    protected function main() {
        foreach ($this->transactions as $transaction) {
            // Could throw some PDO or MYSQL exception, but that is handled by the AbstractWorker
            $stmt = $this->pdo->query("UPDATE transactions SET processed = 1 WHERE id = {$transaction['id']} LIMIT 1");
            $stmt->execute();
        }
    }
}

Как вы можете видеть, TransactionProcessorWorker был довольно прост в реализации, поскольку нам нужно было указать ограничение на память и беспокоиться о действительных действиях, которые ему нужно выполнить. Обработка TransactionProcessorWorker требуется в TransactionProcessorWorker потому что это обрабатывается в AbsractWorker .

Важная заметка

При наследовании от абстрактного класса все методы, помеченные как абстрактные в объявлении класса родителя, должны быть определены дочерним элементом (или сам ребенок также должен быть отмечен как абстрактный); кроме того, эти методы должны быть определены с той же (или менее ограниченной) видимостью. Например, если абстрактный метод определяется как защищенный, реализация функции должна быть определена как защищенная или общедоступная, но не закрытая.

Взято из документации PHP для абстракции класса .

Если вы не определяете родительские методы абстрактных классов в дочернем классе, вы будете подвергнуты Fatal PHP Error, как показано ниже.

Неустранимая ошибка: класс X содержит 1 абстрактный метод и поэтому должен быть объявлен абстрактным или реализовать оставшиеся методы (X :: x) в

Распространение имен и автозагрузка

Технически, автозагрузка работает, выполняя обратный вызов, когда требуется класс PHP, но не найден. Такие обратные вызовы обычно пытаются загрузить эти классы.

Как правило, автозагрузку можно понимать как попытку загрузить файлы PHP (особенно файлы классов PHP, где исходный PHP-файл предназначен для определенного класса) из соответствующих путей в соответствии с полным именем класса (FQN), когда класс необходим ,

Предположим, что у нас есть эти классы:

Файл класса для application\controllers\Base :

<?php
namespace application\controllers { class Base {...} }

Файл класса для application\controllers\Control :

<?php
namespace application\controllers { class Control {...} }

Файл класса для application\models\Page :

<?php
namespace application\models { class Page {...} }

В исходной папке эти классы должны быть помещены на пути как их FQN соответственно:

  • Папка источника
    • applications
      • controllers
        • Base.php
        • Control.php
      • models
        • Page.php

Такой подход позволяет программно разрешать путь к файлу класса в соответствии с FQN, используя эту функцию:

function getClassPath(string $sourceFolder, string $className, string $extension = ".php") {
    return $sourceFolder . "/" . str_replace("\\", "/", $className) . $extension; // note that "/" works as a directory separator even on Windows
}

Функция spl_autoload_register позволяет нам загружать класс при необходимости с помощью пользовательской функции:

const SOURCE_FOLDER = __DIR__ . "/src";
spl_autoload_register(function (string $className) {
    $file = getClassPath(SOURCE_FOLDER, $className);
    if (is_readable($file)) require_once $file;
});

Эта функция может быть дополнительно расширена для использования резервных методов загрузки:

const SOURCE_FOLDERS = [__DIR__ . "/src", "/root/src"]);
spl_autoload_register(function (string $className) {
    foreach(SOURCE_FOLDERS as $folder) {
        $extensions = [
            // do we have src/Foo/Bar.php5_int64?
            ".php" . PHP_MAJOR_VERSION . "_int" . (PHP_INT_SIZE * 8),
            // do we have src/Foo/Bar.php7?
            ".php" . PHP_MAJOR_VERSION,
            // do we have src/Foo/Bar.php_int64?
            ".php" . "_int" . (PHP_INT_SIZE * 8),
            // do we have src/Foo/Bar.phps?
            ".phps"
            // do we have src/Foo/Bar.php?
            ".php"
        ];
        foreach($extensions as $ext) {
            $path = getClassPath($folder, $className, $extension);
            if(is_readable($path)) return $path;
        }
    }
});

Обратите внимание: PHP не пытается загружать классы всякий раз, когда загружается файл, который использует этот класс. Он может быть загружен в середине скрипта или даже в функциях выключения. Это одна из причин, почему разработчикам, особенно тем, кто использует автозагрузку, следует избегать замены исполняемых исходных файлов во время выполнения, особенно в файлах phar.

Динамическое связывание

Динамическое связывание, также называемое переопределением метода, является примером полиморфизма времени выполнения, который возникает, когда несколько классов содержат разные реализации одного и того же метода, но объект, к которому будет вызван метод, неизвестен до времени выполнения .

Это полезно, если определенное условие определяет, какой класс будет использоваться для выполнения действия, где действие называется одинаковым в обоих классах.

interface Animal {
    public function makeNoise();
}

class Cat implements Animal {
    public function makeNoise
    {
        $this->meow();
    }
    ...
}

class Dog implements Animal {
    public function makeNoise {
        $this->bark();
    }
    ...
}

class Person {
    const CAT = 'cat';
    const DOG = 'dog';

    private $petPreference;
    private $pet;

    public function isCatLover(): bool {
        return $this->petPreference == self::CAT;
    }

    public function isDogLover(): bool {
        return $this->petPreference == self::DOG;
    }

    public function setPet(Animal $pet) {
        $this->pet = $pet;
    }

    public function getPet(): Animal {
        return $this->pet;
    }
}

if($person->isCatLover()) {
    $person->setPet(new Cat());
} else if($person->isDogLover()) {
    $person->setPet(new Dog());
}

$person->getPet()->makeNoise();

В приведенном выше примере класс Animal ( Dog|Cat ), который будет makeNoise , неизвестен до времени выполнения в зависимости от свойства в классе User .

Видимость метода и свойств

Существует три типа видимости, которые можно применять к методам (функции класса / объекта ) и свойствам ( переменные класса / объекта ) в классе, которые обеспечивают контроль доступа для метода или свойства, к которому они применяются.

Вы можете подробно ознакомиться с ними в документации PHP для видимости ООП .

общественного

Объявление метода или свойства как public позволяет доступ к способу или свойствам посредством:

  • Класс, который его объявил.
  • Классы, которые расширяют объявленный класс.
  • Любые внешние объекты, классы или код вне иерархии классов.

Примером такого public доступа будет:

class MyClass {
    // Property
    public $myProperty = 'test';

    // Method
    public function myMethod() {
        return $this->myProperty;
    }
}

$obj = new MyClass();
echo $obj->myMethod();
// Out: test

echo $obj->myProperty;
// Out: test

защищенный

Объявление метода или свойства как protected позволяет доступ к способу или свойствам посредством:

  • Класс, который его объявил.
  • Классы, которые расширяют объявленный класс.

Это не позволяет внешним объектам, классам или кодам за пределами иерархии классов обращаться к этим методам или свойствам. Если что-то, использующее этот метод / свойство, не имеет к нему доступа, оно не будет доступно, и будет вызвана ошибка. Доступ к нему имеют только экземпляры объявленного себя (или подклассы).

Примером этого protected доступа будет:

class MyClass {
    protected $myProperty = 'test';

    protected function myMethod() {
        return $this->myProperty;
    }
}

class MySubClass extends MyClass {
    public function run() {
        echo $this->myMethod();
    }
}

$obj = new MySubClass();
$obj->run(); // This will call MyClass::myMethod();
// Out: test

$obj->myMethod(); // This will fail.
// Out: Fatal error: Call to protected method MyClass::myMethod() from context ''

В приведенном выше примере отмечается, что вы можете получить доступ только к protected элементам внутри своей собственной области. По существу: «В доме есть доступ только из дома».


Частный

Объявление метода или свойства как private позволяет доступ к способу или свойствам посредством:

  • Класс, который объявил это только (не подклассы).

private метод или свойство только видимы и доступны внутри класса, который его создал.

Обратите внимание, что объекты того же типа будут иметь доступ к каждому другому частному и защищенному членам, даже если они не совпадают с экземплярами.

class MyClass {
    private $myProperty = 'test';

    private function myPrivateMethod() {
        return $this->myProperty;
    }

    public function myPublicMethod() {
        return $this->myPrivateMethod();
    }

    public function modifyPrivatePropertyOf(MyClass $anotherInstance) {
        $anotherInstance->myProperty = "new value";
    }
}

class MySubClass extends MyClass {
    public function run() {
        echo $this->myPublicMethod();
    }

    public function runWithPrivate() {
        echo $this->myPrivateMethod();
    }
}

$obj = new MySubClass();
$newObj = new MySubClass();

// This will call MyClass::myPublicMethod(), which will then call
// MyClass::myPrivateMethod();
$obj->run(); 
// Out: test

    
$obj->modifyPrivatePropertyOf($newObj);

$newObj->run();
// Out: new value

echo $obj->myPrivateMethod(); // This will fail.
// Out: Fatal error: Call to private method MyClass::myPrivateMethod() from context ''

echo $obj->runWithPrivate(); // This will also fail.
// Out: Fatal error: Call to private method MyClass::myPrivateMethod() from context 'MySubClass'

Как уже отмечалось, вы можете получить доступ только к private методу / свойству из его определенного класса.

Вызов родительского конструктора при создании экземпляра дочернего элемента

Общей ошибкой дочерних классов является то, что если ваш родитель и ребенок содержат конструктор ( __construct() ), будет запускаться только конструктор дочернего класса . Могут быть случаи, когда вам нужно запустить родительский __construct() из его дочернего элемента. Если вам нужно это сделать, вам нужно будет использовать разрешение parent:: scope:

parent::__construct();

Теперь использование этого в реальной ситуации будет выглядеть примерно так:

class Foo {

    function __construct($args) { 
        echo 'parent'; 
    }

}

class Bar extends Foo {

    function __construct($args) {
        parent::__construct($args);
    }
}

Вышеупомянутый __construct() будет запускать родительский __construct() результате которого выполняется echo .

Конечное ключевое слово

Def: Final Keyword не позволяет дочерним классам переопределять метод, префикс определения с помощью final. Если сам класс определяется окончательным, то он не может быть расширен

Конечный метод

class BaseClass {
   public function test() {
       echo "BaseClass::test() called\n";
   }
   
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called\n";
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo "ChildClass::moreTesting() called\n";
   }
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()

Конечный класс:

final class BaseClass {
   public function test() {
       echo "BaseClass::test() called\n";
   }

   // Here it doesn't matter if you specify the function as final or not
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called\n";
   }
}

class ChildClass extends BaseClass {
}
// Results in Fatal error: Class ChildClass may not inherit from final class (BaseClass)

Конечные константы. В отличие от Java, ключевое слово final не используется для констант класса в PHP. Вместо этого используйте ключевое слово const .

Почему я должен использовать final ?

  1. Предотвращение массивной цепи наследования гибели
  2. Поощрение композиции
  3. Заставить разработчика задуматься о пользовательском публичном API
  4. Принудите разработчика к сокращению открытого API объекта
  5. final класс всегда можно сделать расширяемым
  6. extends инкапсуляцию разрывов
  7. Вам не нужна такая гибкость
  8. Вы можете изменить код

Когда следует избегать final : окончательные занятия работают эффективно только при следующих предположениях:

  1. Существует абстракция (интерфейс), которую конечный класс реализует
  2. Весь публичный API конечного класса является частью этого интерфейса

$ this, self и static plus singleton

Используйте $this для обозначения текущего объекта. Используйте self для обозначения текущего класса. Другими словами, используйте $this->member для нестатических членов, используйте self::$member для статических членов.

В приведенном ниже примере sayHello() и sayGoodbye() используют self и $this разницу можно наблюдать здесь.

class Person {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }

    public function getTitle() {
        return $this->getName()." the person";
    }

    public function sayHello() {
        echo "Hello, I'm ".$this->getTitle()."<br/>";
    }

    public function sayGoodbye() {
        echo "Goodbye from ".self::getTitle()."<br/>";
    }
}

class Geek extends Person {
    public function __construct($name) {
        parent::__construct($name);
    }

    public function getTitle() {
        return $this->getName()." the geek";
    }
}

$geekObj = new Geek("Ludwig");
$geekObj->sayHello();
$geekObj->sayGoodbye();

static ссылается на любой класс в иерархии, которую вы назвали методом on. Это позволяет лучше использовать статические свойства класса при наследовании классов.

Рассмотрим следующий код:

class Car {
    protected static $brand = 'unknown';
    
    public static function brand() {
         return self::$brand."\n";
    }
}

class Mercedes extends Car {
    protected static $brand = 'Mercedes';
}

class BMW extends Car {
    protected static $brand = 'BMW';
}

echo (new Car)->brand();
echo (new BMW)->brand();
echo (new Mercedes)->brand();

Это не приводит к желаемому результату:

неизвестный
неизвестный
неизвестный

Это потому, что self относится к классу Car всякий раз, когда вызывается метод brand() .

Чтобы обратиться к правильному классу, вам нужно использовать static :

class Car {
    protected static $brand = 'unknown';
    
    public static function brand() {
         return static::$brand."\n";
    }
}

class Mercedes extends Car {
    protected static $brand = 'Mercedes';
}

class BMW extends Car {
    protected static $brand = 'BMW';
}

echo (new Car)->brand();
echo (new BMW)->brand();
echo (new Mercedes)->brand();

Это дает желаемый результат:

неизвестный
БМВ
Mercedes

См. Также Поздняя статическая привязка

Синглтон

Если у вас есть объект, который стоит создавать или представляет соединение с каким-либо внешним ресурсом, который вы хотите повторно использовать, то есть соединение с базой данных, в котором нет пула соединений или сокета в какой-либо другой системе, вы можете использовать static и self ключевые слова в класс, чтобы сделать его одиночным. Есть сильные мнения о том, следует ли использовать одноэлементный шаблон или не использовать, но он действительно использует его.

class Singleton {
    private static $instance = null;

    public static function getInstance(){
        if(!isset(self::$instance)){
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    private function __construct() {
        // Do constructor stuff
    }
}

Как вы можете видеть в примере кода, мы определяем приватное статическое свойство $instance для хранения ссылки на объект. Поскольку это статично, эта ссылка используется для всех объектов этого типа.

Метод getInstance() использует метод, известный как ленивый экземпляр, чтобы отложить создание объекта до последнего возможного момента, поскольку вы не хотите, чтобы в памяти не использовались неиспользуемые объекты, которые никогда не предназначались для использования. Это также экономит время, и процессор на загрузке страницы не загружает больше объектов, чем необходимо. Метод проверяет, установлен ли объект, создавая его, если нет, и возвращает его. Это гарантирует, что только один объект такого типа будет создан.

Мы также устанавливаем конструктор как частный, чтобы гарантировать, что никто не создает его с new ключевым словом извне. Если вам нужно наследовать от этого класса, просто измените private ключевые слова на protected .

Чтобы использовать этот объект, вы просто пишете следующее:

$singleton = Singleton::getInstance();

Теперь я умоляю вас использовать инъекцию зависимостей, где вы можете, и стремиться к слабосвязанным объектам, но иногда это просто неразумно, и одноэлементный шаблон может быть полезен.

Автозагрузка

Никто не хочет require или include каждый раз, когда используется класс или наследование. Поскольку это может быть болезненным и легко забыть, PHP предлагает так называемую автозагрузку. Если вы уже используете Composer, прочитайте об автозагрузке с помощью Composer .

Что такое автозагрузка?

Название в основном говорит все. Вам не нужно , чтобы получить файл , когда запрашиваемое класс хранится в, но PHP автоматически матически нагрузки все.

Как я могу сделать это в базовом PHP без стороннего кода?

Существует функция __autoload , но считается, что лучше использовать spl_autoload_register . Эти функции будут рассмотрены PHP каждый раз, когда класс не определен в данном пространстве. Поэтому добавление автозагрузки в существующий проект не представляет проблемы, поскольку определенные классы (через require ie) будут работать, как раньше. Для обеспечения точности в следующих примерах будут использоваться анонимные функции, если вы используете PHP <5.3, вы можете определить функцию и передать ее имя в качестве аргумента spl_autoload_register .

Примеры

spl_autoload_register(function ($className) {
    $path = sprintf('%s.php', $className);
    if (file_exists($path)) {
        include $path;
    } else {
        // file not found
    }
});

Вышеприведенный код просто пытается включить имя файла с именем класса и добавленным расширением «.php», используя sprintf . Если FooBar необходимо загрузить, он выглядит, если FooBar.php существует, и если он включает его.

Конечно, это можно расширить, чтобы соответствовать индивидуальным потребностям проекта. Если _ внутри имени класса используется для группировки, например User_Post и User_Image оба относятся к User , оба класса могут храниться в папке «Пользователь», например:

spl_autoload_register(function ($className) {
    //                        replace _ by / or \ (depending on OS)
    $path = sprintf('%s.php', str_replace('_', DIRECTORY_SEPARATOR, $className) );
    if (file_exists($path)) {
        include $path;
    } else {
        // file not found
    }
});

Класс User_Post теперь будет загружен из «User / Post.php» и т. Д.

spl_autoload_register может быть адаптирован к различным потребностям. Все ваши файлы с классами называются «class.CLASSNAME.php»? Нет проблем. Различная вложенность ( User_Post_Content => «Пользователь / Сообщение / Содержание.php»)? Нет проблем.

Если вам нужен более сложный механизм автозагрузки - и вы не хотите включать Composer, вы можете работать без добавления сторонних библиотек.

spl_autoload_register(function ($className) {
    $path = sprintf('%1$s%2$s%3$s.php',
        // %1$s: get absolute path
        realpath(dirname(__FILE__)),
        // %2$s: / or \ (depending on OS)
        DIRECTORY_SEPARATOR,
        // %3$s: don't wory about caps or not when creating the files
        strtolower(
            // replace _ by / or \ (depending on OS)
            str_replace('_', DIRECTORY_SEPARATOR, $className)
        )
    );

    if (file_exists($path)) {
        include $path;
    } else {
        throw new Exception(
            sprintf('Class with name %1$s not found. Looked in %2$s.',
                $className,
                $path
            )
        );
    }
});

Используя автозагрузчики, подобные этому, вы можете с радостью написать такой код:

require_once './autoload.php'; // where spl_autoload_register is defined

$foo = new Foo_Bar(new Hello_World());

Использование классов:

class Foo_Bar extends Foo {}
class Hello_World implements Demo_Classes {}

Эти примеры будут включать классы из foo/bar.php , foo.php , hello/world.php и demo/classes.php .

Анонимные классы

Анонимные классы были введены в PHP 7, чтобы можно было легко создавать быстрые одноразовые объекты. Они могут принимать аргументы конструктора, расширять другие классы, реализовывать интерфейсы и использовать черты так же, как обычные классы.

В своей основной форме анонимный класс выглядит следующим образом:

new class("constructor argument") {
    public function __construct($param) {
        var_dump($param);
    }
}; // string(20) "constructor argument"

Вложение анонимного класса внутри другого класса не дает ему доступа к закрытым или защищенным методам или свойствам этого внешнего класса. Доступ к защищенным методам и свойствам внешнего класса может быть получен путем расширения внешнего класса из анонимного класса. Доступ к приватным свойствам внешнего класса можно получить, передав их конструктору анонимного класса.

Например:

class Outer {
    private $prop = 1;
    protected $prop2 = 2;

    protected function func1() {
        return 3;
    }

    public function func2() {
        // passing through the private $this->prop property
        return new class($this->prop) extends Outer {
            private $prop3;

            public function __construct($prop) {
                $this->prop3 = $prop;
            }

            public function func3() {
                // accessing the protected property Outer::$prop2
                // accessing the protected method Outer::func1()
                // accessing the local property self::$prop3 that was private from Outer::$prop
                return $this->prop2 + $this->func1() + $this->prop3;
            }
        };
    }
}

echo (new Outer)->func2()->func3(); // 6

Определение базового класса

Объект в PHP содержит переменные и функции. Объекты обычно принадлежат классу, который определяет переменные и функции, которые будут содержать все объекты этого класса.

Синтаксис для определения класса:

class Shape {
    public $sides = 0;
    
    public function description() {
        return "A shape with $this->sides sides.";
    }
}

Как только класс определен, вы можете создать экземпляр, используя:

$myShape = new Shape();

Доступ к переменным и функциям объекта осуществляется следующим образом:

$myShape = new Shape();
$myShape->sides = 6;

print $myShape->description(); // "A shape with 6 sides"

Конструктор

Классы могут определять специальный __construct() , который выполняется как часть создания объекта. Это часто используется для указания начальных значений объекта:

class Shape {
    public $sides = 0;
    
    public function __construct($sides) {
        $this->sides = $sides;
    }
    
    public function description() {
        return "A shape with $this->sides sides.";
    }
}

$myShape = new Shape(6);

print $myShape->description(); // A shape with 6 sides

Расширение другого класса

Определения классов могут расширять существующие определения классов, добавлять новые переменные и функции, а также изменять значения, определенные в родительском классе.

Вот класс, который расширяет предыдущий пример:

class Square extends Shape {
    public $sideLength = 0;
    
    public function __construct($sideLength) {
       parent::__construct(4);
       
       $this->sideLength = $sideLength;
    }
    
    public function perimeter() {
        return $this->sides * $this->sideLength;
    }

    public function area() {
        return $this->sideLength * $this->sideLength;
    }
}

Класс Square содержит переменные и поведение как для класса Shape и для класса Square :

$mySquare = new Square(10);

print $mySquare->description()/ // A shape with 4 sides

print $mySquare->perimeter() // 40

print $mySquare->area() // 100


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow