Php вызов родительского конструктора

Обновлено: 27.04.2024

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

15. Объектно-ориентированное программирование

15.1. Классы и объекты. Поля и методы

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

Объект - это экземпляр класса.

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

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

15.2. Области видимости

Для полей и методов можно задавать области видимости

  • public — свойства или методы, объявленные как public, могут быть доступны в любом месте.
  • protected — protected свойства и методы доступны внутри класса, а также в дочерних классах.
  • private — доступ к private свойствам и методам имеет только класс, в котором эти свойства или методы объявлены

15.3. Переменная $this

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

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

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

Конструкторы при наследовании

Конструкторы, определённые в классах-родителях не вызываются автоматически, если дочерний класс определяет собственный конструктор. Чтобы вызвать конструктор, объявленный в родительском классе, требуется вызвать parent::__construct() внутри конструктора дочернего класса. Если в дочернем классе не определён конструктор, то он может быть унаследован от родительского класса как обычный метод (если он не был определён как приватный).

Приватный конструктор. Паттерн Singleton

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

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

15.5. Динамический вызов свойств и методов

15.6. Статические свойства и методы. Константы

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

Чтобы объявить метод статическим, нужно после модификатора доступа (то есть после public, private или protected) написать ключевое слово static.

Статические свойства принадлежат не какому-то объекту класса, а самому классу, хотя объекты класса и имеют доступ к этим свойствам. Внутри статических методов нельзя использовать $this .

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

Метод __set() будет выполнен при записи данных в недоступные (защищённые или приватные) или несуществующие свойства.

Метод __get() будет выполнен при чтении данных из недоступных (защищённых или приватных) или несуществующих свойств.

Метод __call() запускается при вызове недоступных методов в контексте объект.

Метод __toString() позволяет классу решать, как он должен реагировать при преобразовании в строку. Например, что вывести при выполнении echo $obj; .

Полный список магических методов: __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __serialize(), __unserialize(), __toString(), __invoke(), __set_state(), __clone() и __debugInfo()

Обращение к родительскому классу из наследника

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

Перезапись конструктора родительского класса

15.9. Наследование vs композиция и аггрегация

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

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

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

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

У такого подхода есть один недостаток - сильная связанность, это значит, что, для того чтобы поменять класс A на A1, вам придется переписывать конструктор (new A1 вместо A).

Преимущество у такого способа, это то, что класс B, управляет временем жизни объекта A. Т.е. при удалении объекта B будет и удален, объект A который был создан внутри B.

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

В отличие от композиции, тут все наоборот. Преимущество: легко передать новый объект A1 без изменений в коде - слабая связанность. Из недостатков, пожалуй, следует отметить, что в больших системах это приводит к огромному кол-ву переменных в конструкторе

Композиция лучше наследования

Как говорится в известной книге "Шаблоны проектирования" Банды четырёх, по мере возможности нужно выбирать композицию, а не наследование. Есть много хороших причин использовать как наследование, так и композицию. Главная цель этой максимы заключается в том, если вы инстинктивно склоняетесь к наследованию, то постарайтесь представить, может ли композиция лучше решить вашу задачу. В каких-то случаях это действительно более подходящий вариант.

Вы спросите: "А когда лучше выбирать наследование?" Всё зависит от конкретной задачи, но можно ориентироваться на этот список ситуаций, когда наследование предпочтительнее композиции:

  • Ваше наследование — это взаимосвязь is-a, а не has-a. Пример: Человек → Животное vs. Пользователь → Детали пользователя (UserDetails).
  • Вы можете повторно использовать код из базовых классов. (Люди могут двигаться, как животные.)
  • Вы хотите внести глобальные изменения в производные классы, изменив базовый класс. (Изменение расхода калорий у животных во время движения.)

15.11. Трейты вместо множественного наследования

В PHP нельзя наследовать от нескольких классов сразу, только от одного.

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

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

Синтаксис трейта такой же как и у класса, за исключением того, что имя трейта нужно объявлять с помощью ключевого слова trait.

Экземпляр трейта нельзя создать - трейты предназначены только для подключения к другим классам.

15.12. Абстрактные классы и интерфейсы. Полиморфизм

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

Пусть у вас есть класс User, а от него наследуют классы Employee и Student.

При этом предполагается, что вы будете создавать объекты классов Employee и Student, но объекты класса User - не будете, так как сам класс User используется только для группировки общих свойств и методов своих наследников.

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

Для того, чтобы объявить класс абстрактным, нужно при его объявлении написать ключевое слово abstract

Абстрактные классы также могут содержать абстрактные методы.

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

А собственно реализация таких методов - уже задача потомков.

Для того, чтобы объявить метод абстрактным, при его объявлении следует написать ключевое слово abstract.

При наследовании от абстрактного класса, все методы, помеченные абстрактными в родительском классе, должны быть определены в дочернем классе.

При этом область видимости этих методов должна совпадать или быть менее строгой. Что значит менее строгой: например, если абстрактный метод объявлен как protected, то реализация этого метода должна быть protected или public, но не private.

Объявления методов также должны совпадать: количество обязательных параметром должно быть одинаковым. Однако класс-потомок может добавлять необязательные параметры, которые не были указаны при объявлении метода в родителе.

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

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

Зачем нам такое нужно: чтобы при программировании совершать меньше ошибок - описав все необходимые методы в классе-родителе, мы можем быть уверенны в том, что все потомки их действительно реализуют.

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

В PHP пространства имён используются для решения двух проблем, с которыми сталкиваются авторы библиотек и приложений при создании повторно используемых элементов кода, таких как классы и функции:

  • Конфликт имён между вашим кодом и внутренними классами/функциями/константами PHP или сторонними.
  • Возможность создавать псевдонимы (или сокращения) для Ну_Очень_Длинных_Имён, чтобы облегчить первую проблему и улучшить читаемость исходного кода.

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

Для класса Page из файла /admin/page.php укажем пространство имен Admin:

А для класса Page из файла файл /users/page.php укажем пространство имен Users:

Давайте теперь в файле /index.php создадим объект одного и второго класса Page:

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

15.14. Автозагрузка через spl_autoload_register

Большинство разработчиков объектно-ориентированных приложений используют такое соглашение именования файлов, в котором каждый класс хранится в отдельно созданном для него файле. Одна из самых больших неприятностей - необходимость писать в начале каждого скрипта длинный список подгружаемых файлов (по одному для каждого класса).

Composer - это менеджер зависимостей для PHP. Вы можете описать от каких библиотек зависит ваш проект и Composer установит нужные библиотеки за вас.

При установке php пакетов Composer не просто устанавливает их, он также устанавливает все зависимости, от которых эти пакеты зависят. Т.е., например, если загружаемая библиотека будет зависеть от 3 других пакетов, а каждая из них, ещё в свою очередь от нескольких, то Composer всё это установит автоматически.

Загрузку сторонних библиотек Composer выполняет в папку vendor , которую данный php скрипт создаёт в корневой директории проекта. Также composer создаёт специальный файл autoload.php . Если вы подключите этот файл в проекте, вы сразу сможете использовать все загруженные библиотеки.

Пример: вызываем в консоли команду composer require monolog/monolog

Появятся файлы composer.json , composer.lock и папка vendor .

Файл composer.json - это главный файл Composer. В нем содержится описание основных пакетов, включая требования к их версиям.

Файл composer.lock - это файл, содержащий уже не требования, а реальные версии пакетов, которые были установлены на компьютер пользователя. Основное назначение файла composer.lock заключается в полном сохранении среды, в которой осуществлялась разработка и тестирование проекта. Например, если вы захотите скопировать проект в какое-то другое место без переноса файла composer.lock , то выполнив в нём команду composer install , вы можете получить другие версии пакетов. Это может случиться из-за выхода новых версий основных пакетов, описанных в файле composer.json , их зависимостей, зависимостей их зависимостей и т.д.

Если же вы разворачиваете проект, включающий в себя composer.lock , то вы получите те же версии зависимостей, которые были при разработке и тестировании. Поэтому composer.lock необходимо добавлять в git.

В папке vendor располагаются исходники библиотек и файл autoload.php . Вы можете подключить autoload.php и начать использовать классы, которые эти библиотеки предоставляют:

15.16. Автозагрузка собственных классов с помощью composer. Стандарт PSR-4

Вы даже можете добавить свой код в автозагрузчик, добавив поле autoload в composer.json

После добавления поля autoload в composer.json необходимо повторно выполнить команду dump-autoload для повторной генерации файла vendor/autoload.php

Composer зарегистрирует автозагрузчик PSR-4 для пространства имен App .

В таблице ниже представлены примеры соответствий полностью определённого имени класса, префикса пространства имён, базового каталога и итогового пути к файлу.

Fully Qualified Class Name Namespace Prefix Base Directory Resulting File Path
\Acme\Log\Writer\File_Writer Acme\Log\Writer ./acme-log-writer/lib/ ./acme-log-writer/lib/File_Writer.php
\Aura\Web\Response\Status Aura\Web /path/to/aura-web/src/ /path/to/aura-web/src/Response/Status.php
\Symfony\Core\Request Symfony\Core ./vendor/Symfony/Core/ ./vendor/Symfony/Core/Request.php
\Zend\Acl Zend /usr/includes/Zend/ /usr/includes/Zend/Acl.php

15.17 Reflection API

Рефлексия (отражение) - процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения. PHP включает в себя полноценный Reflection API, который предоставляет возможность проводить интроспекцию классов, интерфейсов, функций, методов и модулей. Кроме того, Reflection API позволяет получать doc-блоки комментариев функций, классов и методов.

Класс Profile является черным ящиком. И если у нас нет доступа к коду, то посмотреть как он устроен можно используя ReflectionClass:

ReflectionClass похож на аналитика для класса Profile, и это основная идея Reflection API.

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

ReflectionClass: сообщает информацию о классе. ReflectionFunction: сообщает информацию о функции. ReflectionParameter: извлекает информацию о параметрах функции или метода. ReflectionClassConstant: сообщает информацию о константе класса.

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

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

15.18 SPL - Standart PHP Library

Стандартная библиотека PHP (SPL) - это набор интерфейсов и классов, предназначенных для решения стандартных задач.

SPL предоставляет ряд стандартных структур данных, итераторов для итерирования объектов, интерфейсов, стандартных исключений, некоторое количество классов для работы с файлами и предоставляет ряд функций, например spl_autoload_register().

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

Basic class definitions begin with the keyword class , followed by a class name, followed by a pair of curly braces which enclose the definitions of the properties and methods belonging to the class.

The class name can be any valid label, provided it is not a PHP reserved word. A valid class name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it would be expressed thus: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$ .

A class may contain its own constants, variables (called "properties"), and functions (called "methods").

class SimpleClass
// property declaration
public $var = 'a default value' ;

// method declaration
public function displayVar () echo $this -> var ;
>
>
?>

The pseudo-variable $this is available when a method is called from within an object context. $this is the value of the calling object.

Calling a non-static method statically throws an Error . Prior to PHP 8.0.0, this would generate a deprecation notice, and $this would be undefined.

class A
function foo ()
if (isset( $this )) echo '$this is defined (' ;
echo get_class ( $this );
echo ")\n" ;
> else echo "\$this is not defined.\n" ;
>
>
>

$a = new A ();
$a -> foo ();

$b = new B ();
$b -> bar ();

Output of the above example in PHP 7:

Output of the above example in PHP 8:

To create an instance of a class, the new keyword must be used. An object will always be created unless the object has a constructor defined that throws an exception on error. Classes should be defined before instantiation (and in some cases this is a requirement).

If a string containing the name of a class is used with new , a new instance of that class will be created. If the class is in a namespace, its fully qualified name must be used when doing this.

Note:

If there are no arguments to be passed to the class's constructor, parentheses after the class name may be omitted.

// This can also be done with a variable:
$className = 'SimpleClass' ;
$instance = new $className (); // new SimpleClass()
?>

As of PHP 8.0.0, using new with arbitrary expressions is supported. This allows more complex instantiation if the expression produces a string . The expressions must be wrapped in parentheses.

In the given example we show multiple examples of valid arbitrary expressions that produce a class name. This shows a call to a function, string concatenation, and the ::class constant.

class ClassA extends \ stdClass <>
class ClassB extends \ stdClass <>
class ClassC extends ClassB <>
class ClassD extends ClassA <>

function getSomeClass (): string
return 'ClassA' ;
>

var_dump (new ( getSomeClass ()));
var_dump (new ( 'Class' . 'B' ));
var_dump (new ( 'Class' . 'C' ));
var_dump (new ( ClassD ::class));
?>

Output of the above example in PHP 8:

In the class context, it is possible to create a new object by new self and new parent .

When assigning an already created instance of a class to a new variable, the new variable will access the same instance as the object that was assigned. This behaviour is the same when passing instances to a function. A copy of an already created object can be made by cloning it.

$instance = new SimpleClass ();

$assigned = $instance ;
$reference =& $instance ;

$instance -> var = '$assigned will have this value' ;

$instance = null ; // $instance and $reference become null

var_dump ( $instance );
var_dump ( $reference );
var_dump ( $assigned );
?>

The above example will output:

It's possible to create instances of an object in a couple of ways:

class Test
static public function getNew ()
return new static;
>
>

class Child extends Test
<>

$obj1 = new Test ();
$obj2 = new $obj1 ;
var_dump ( $obj1 !== $obj2 );

$obj3 = Test :: getNew ();
var_dump ( $obj3 instanceof Test );

$obj4 = Child :: getNew ();
var_dump ( $obj4 instanceof Child );
?>

The above example will output:

It is possible to access a member of a newly created object in a single expression:

The above example will output something similar to:

Note: Prior to PHP 7.1, the arguments are not evaluated if there is no constructor function defined.

Properties and methods

Class properties and methods live in separate "namespaces", so it is possible to have a property and a method with the same name. Referring to both a property and a method has the same notation, and whether a property will be accessed or a method will be called, solely depends on the context, i.e. whether the usage is a variable access or a function call.

public function bar () return 'method' ;
>
>

$obj = new Foo ();
echo $obj -> bar , PHP_EOL , $obj -> bar (), PHP_EOL ;

The above example will output:

That means that calling an anonymous function which has been assigned to a property is not directly possible. Instead the property has to be assigned to a variable first, for instance. It is possible to call such a property directly by enclosing it in parentheses.

public function __construct () $this -> bar = function() return 42 ;
>;
>
>

echo ( $obj -> bar )(), PHP_EOL ;

The above example will output:

extends

A class can inherit the constants, methods, and properties of another class by using the keyword extends in the class declaration. It is not possible to extend multiple classes; a class can only inherit from one base class.

The inherited constants, methods, and properties can be overridden by redeclaring them with the same name defined in the parent class. However, if the parent class has defined a method or constant as final, they may not be overridden. It is possible to access the overridden methods or static properties by referencing them with parent::.

Note: As of PHP 8.1.0, constants may be declared as final.

class ExtendClass extends SimpleClass
// Redefine the parent method
function displayVar ()
echo "Extending class\n" ;
parent :: displayVar ();
>
>

$extended = new ExtendClass ();
$extended -> displayVar ();
?>

The above example will output:

Signature compatibility rules

When overriding a method, its signature must be compatible with the parent method. Otherwise, a fatal error is emitted, or, prior to PHP 8.0.0, an E_WARNING level error is generated. A signature is compatible if it respects the variance rules, makes a mandatory parameter optional, and if any new parameters are optional. This is known as the Liskov Substitution Principle, or LSP for short. The constructor, and private methods are exempt from these signature compatibility rules, and thus won't emit a fatal error in case of a signature mismatch.

class Base
public function foo ( int $a ) echo "Valid\n" ;
>
>

class Extend1 extends Base
function foo ( int $a = 5 )
parent :: foo ( $a );
>
>

class Extend2 extends Base
function foo ( int $a , $b = 5 )
parent :: foo ( $a );
>
>

$extended1 = new Extend1 ();
$extended1 -> foo ();
$extended2 = new Extend2 ();
$extended2 -> foo ( 1 );

The above example will output:

The following examples demonstrate that a child method which removes a parameter, or makes an optional parameter mandatory, is not compatible with the parent method.

class Base
public function foo ( int $a = 5 ) echo "Valid\n" ;
>
>

class Extend extends Base
function foo ()
parent :: foo ( 1 );
>
>

Output of the above example in PHP 8 is similar to:

class Base
public function foo ( int $a = 5 ) echo "Valid\n" ;
>
>

class Extend extends Base
function foo ( int $a )
parent :: foo ( $a );
>
>

Output of the above example in PHP 8 is similar to:

Renaming a method's parameter in a child class is not a signature incompatibility. However, this is discouraged as it will result in a runtime Error if named arguments are used.

class A public function test ( $foo , $bar ) <>
>

class B extends A public function test ( $a , $b ) <>
>

// Pass parameters according to A::test() contract
$obj -> test ( foo : "foo" , bar : "bar" ); // ERROR!

The above example will output something similar to:

::class

The class keyword is also used for class name resolution. To obtain the fully qualified name of a class ClassName use ClassName::class . This is particularly useful with namespaced classes.

The above example will output:

Note:

The class name resolution using ::class is a compile time transformation. That means at the time the class name string is created no autoloading has happened yet. As a consequence, class names are expanded even if the class does not exist. No error is issued in that case.

The above example will output:

As of PHP 8.0.0, the ::class constant may also be used on objects. This resolution happens at runtime, not compile time. Its effect is the same as calling get_class() on the object.

The above example will output:

Nullsafe methods and properties

As of PHP 8.0.0, properties and methods may also be accessed with the "nullsafe" operator instead: ?-> . The nullsafe operator works the same as property or method access as above, except that if the object being dereferenced is null then null will be returned rather than an exception thrown. If the dereference is part of a chain, the rest of the chain is skipped.

The effect is similar to wrapping each access in an is_null() check first, but more compact.

// As of PHP 8.0.0, this line:
$result = $repository ?-> getUser ( 5 )?-> name ;

// Is equivalent to the following code block:
if ( is_null ( $repository )) $result = null ;
> else $user = $repository -> getUser ( 5 );
if ( is_null ( $user )) $result = null ;
> else $result = $user -> name ;
>
>
?>

Note:

The nullsafe operator is best used when null is considered a valid and expected possible value for a property or method return. For indicating an error, a thrown exception is preferable.

User Contributed Notes 13 notes

I was confused at first about object assignment, because it's not quite the same as normal assignment or assignment by reference. But I think I've figured out what's going on.

First, think of variables in PHP as data slots. Each one is a name that points to a data slot that can hold a value that is one of the basic data types: a number, a string, a boolean, etc. When you create a reference, you are making a second name that points at the same data slot. When you assign one variable to another, you are copying the contents of one data slot to another data slot.

Now, the trick is that object instances are not like the basic data types. They cannot be held in the data slots directly. Instead, an object's "handle" goes in the data slot. This is an identifier that points at one particular instance of an obect. So, the object handle, although not directly visible to the programmer, is one of the basic datatypes.

What makes this tricky is that when you take a variable which holds an object handle, and you assign it to another variable, that other variable gets a copy of the same object handle. This means that both variables can change the state of the same object instance. But they are not references, so if one of the variables is assigned a new value, it does not affect the other variable.

// Assignment of an object
Class Object public $foo = "bar" ;
>;

$objectVar = new Object ();
$reference =& $objectVar ;
$assignment = $objectVar

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="bar"
//
?>

$assignment has a different data slot from $objectVar, but its data slot holds a handle to the same object. This makes it behave in some ways like a reference. If you use the variable $objectVar to change the state of the Object instance, those changes also show up under $assignment, because it is pointing at that same Object instance.

$objectVar -> foo = "qux" ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="qux"
//
?>

But it is not exactly the same as a reference. If you null out $objectVar, you replace the handle in its data slot with NULL. This means that $reference, which points at the same data slot, will also be NULL. But $assignment, which is a different data slot, will still hold its copy of the handle to the Object instance, so it will not be NULL.

$objectVar = null ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );

I was wondering if its possible to call the parents __construct(), before the child's __construct() with inheritance in PHP.

Ideally, I would be able to do something in between them. If this is not possible, is there an alternative, which would allow me to do this?

The reason I want to do this is to be able to load a bunch of default settings specific to the Tag that Form can use when __construct() is called.

EDIT: Sorry forgot to add this.. I'd rather not call the parent class from the child class. It's simply because it exposes some private data (for the parent) to the child, when you pass it as an argument

This is what I want to do:

Tag.php

Form.php

Thanks! Matt Mueller

Can you elaborate on what you mean by this 'exposes some private data (for the parent) to the child'?

Private data of a parent class won't be exposed to any subclasses. Public or protected data will, private won't.

Yah, but if the (private) parameters goes through the child constructor first, the child has access to them. I editted it to clarify.

Why is this a problem? Simply have your child constructor pass through the arguments it doesn't care about and ignore them.

Because other people will be extending the Tag and I don't want all this garbage coming through the child class.

4 Answers 4

Just call parent::__construct in the child.

Ahh sorry. I forgot to add that I would rather not have that in the child's constructor. Please read edit.

Decided that this was the best of the alternatives. Unfortunately, its not exactly what I wanted - oh well.. Thanks!

yeah just call parent::__construct() in your construct

This is the way to do, simply read OOP manual if you don't agree. Don't try to reinvent the wheel. If you have concern regarding your model, try to post that too, to see how you did. Probably you are doing a bad design technique.

I reread your edited question, but still don't understand what problems you have. What data is visible to the child class from parent? Maybe you should post a bad example, with some echo to see what do you see wrong.

Yes, but only internally (i.e., by writing a PHP extension), so if I were you I'd settle with calling parent::__construct() . See this section on the PHP wiki.

Sorry, PHP is not Java. I think not requiring (implicitly or explictly) the super constructor to be called was a very poor design decision.

From the sounds of it you may want to rethink your design so that you don't need to pass the parameters in the constructor. If you don't think it can be done, ask it as a question, you might be surprised by some of the suggestions.

The child class has the ability to override the parent constructor without calling it at all. I would recommend having a final method in the parent class. That way everyone knows you don't want this being overriden, and any inherited class (rightly) has access to do whatever it wants in the constructor.

Another, not recommended, more "reinventing the wheel", solution would be to define a function in the parent class, say _construct(), that's called in its own construct. Its not really clear, doesn't use language features/constructs, and is very specific to a single application.

One last thing to keep in mind: you can't really hide information from the child class. With Reflection, serialize, var_dump, var_export and all these other convenient APIs in the php language, if there is code that shouldn't do anything with the data, then there's not really much you can do asides from not store it. There are libraries and such that help create sandboxes, but its hard to sandbox an object from itself.

Edit: Somehow I missed Artefacto's answer, and I suppose he is right (I've never tried writing an extension to do that). Still, implementing it breaks developer expectations while making it harder to actually see code to explain what's going in.

I need to have a class constructor in PHP call its parent's parent's (grandparent?) constructor without calling the parent constructor.

I know this is a bizarre thing to do and I'm attempting to find a means that doesn't smell bad but nonetheless, I'm curious if it's possible.

The Grampa constructor sets properties for itself that are inherited by its children. Papa does some stuff in it's constructor that will mess up Kiddo. So I need the call to Grandpa constructor to set properties for Kiddo during construction.

@MitMaro. I agree and I actually solved my actual problem by creating an intermediate class that extended Grandpa. Then both Papa and Kiddo extended that class. Kiddo required some intermediate functionality of Papa but didn't like it's constructor so the class has that additional functionality and both extend it.

15 Answers 15

The ugly workaround would be to pass a boolean param to Papa indicating that you do not wish to parse the code contained in it's constructor. i.e:

good workaround, but it is not acceptable if the parent class comes from some external library wish you with to extend. I like the too much php answer below.

That's actually quite clever. In my implementation I do if($bypass) return; & can position it so some important stuff gets done before the bypass.

You must use Grandpa::__construct() , there's no other shortcut for it. Also, this ruins the encapsulation of the Papa class - when reading or working on Papa , it should be safe to assume that the __construct() method will be called during construction, but the Kiddo class does not do this.

Can't understand how. Declaring __construct as static results in the following error for me "Fatal error: Constructor Grandpa::__construct() cannot be static" under PHP5.3

When I tried it, I didn't declare it as static. I created the class structure normally but instead of calling parent::__construct(), I called Grandpa::__construct() and it worked. I doesn't seem right to me either but this whole question got kinda weird.

Agreed. I use this all the time - you can call a class by it's name, not just via the magic names of parent or self . I have been known to go three or four classes deep. In fact, I've started referring to my base class by it's name rather than using parent, that way I'm sure I'm getting the right object, always.

@SparK If you are using PHP 5.3.0 or later, Late Static Bindings would most likely solve your use case.

Personally I wouldn't choose to do this as it means Papa's contractor won't get called at all. I'd go with something like cballou's approach (i.e. of passing an argument to Papa's constructor and having it invoke it's parents constructor or not based on that).

The situation we are in here is in such a way that we need to skip the parent's logic and in most of the cases we can't change the grandparent or the parent classes. I believe this is the best way to do it as there are changes made only in the child. The only issue is that it might throw an E_STRICT notice link, it didn't for me though when I tested it.

This is an interesting solution, however, if you really don't need the logic of the parent's constructor, are you sure that you are really making a subsclass of a parent?

This is the correct answer. While it may seem silly to inherit Papa but you want to call the GrandPa constructor without Papa, I've found it useful to be do. I want to keep the hierarchy, but I need to do a clean Kiddo constructor that doesn't have anything to do with Papa, but still want the benefits of using what's going on in GrandPa's constructor. Maybe Papa is doing a bunch of junk in the constructor that isn't needed or wanted by Kiddo, but it still has useful components.

Beautiful solution using Reflection .

I ended up coming up with an alternative solution that solved the problem.

  • I created an intermediate class that extended Grandpa.
  • Then both Papa and Kiddo extended that class.
  • Kiddo required some intermediate functionality of Papa but didn't like it's constructor so the class has that additional functionality and both extend it.

I've upvoted the other two answers that provided valid yet ugly solutions for an uglier question:)

I think the better idea here is to break the functionality you are trying to use out of the constructed and into a protected method. Then you can call that method from a constructor selectively

This does not answer the exact question you've phrased. This happens if the real world muddies up something which should be clear and confined. It's a pity for this question.

Another option that doesn't use a flag and might work in your situation:

I agree with "too much php", try this:

I got the result as expected:

This is a feature not a bug, check this for your reference:

It is just the way it works. If it sees it is coming from the right context this call version does not enforce a static call.

Instead it will simply keep $this and be happy with it.

parent::method() works in the same way, you don't have to define the method as static but it can be called in the same context. Try this out for more interesting:

It also works as expected:

But if you try to initialize a new Papa, you will get an E_STRICT error:

Strict standards: Non-static method Kiddo::hello() should not be called statically, assuming $this from incompatible context

You can use instanceof to determine if you can call a Children::method() in a parent method:

Just remember that parent is only a shortcut to whatever first parent that implemented the method… Thus from a descendant calling AscendantName::method works and from an ascendant calling static::method will always call the latest generation that implemented the method. And you may check not to call yourself using (get_class($this)==__CLASS__?'Healthy people dont\'t call themself…':'Calling latest') if you'd like parents to call kid methods (seems strange but might be usefull combined with private function and __call magic function )…

There's an easier solution for this, but it requires that you know exactly how much inheritance your current class has gone through. Fortunately, get_parent_class()'s arguments allow your class array member to be the class name as a string as well as an instance itself.

Bear in mind that this also inherently relies on calling a class' __construct() method statically, though within the instanced scope of an inheriting object the difference in this particular case is negligible (ah, PHP).

Consider the following:

Again, this isn't a viable solution for a situation where you have no idea how much inheritance has taken place, due to the limitations of debug_backtrace(), but in controlled circumstances, it works as intended.

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

Замечание: Конструкторы в классах-родителях не вызываются автоматически, если класс-потомок определяет собственный конструктор. Чтобы вызвать конструктор, объявленный в родительском классе, следует обратиться к методу parent::__construct() внутри конструктора класса-потомка. Если в классе-потомке не определен конструктор, то он может наследоваться от родительского класса как обычный метод (если он не определен как приватный).

class BaseClass function __construct () print "Конструктор класса BaseClass\n" ;
>
>

class SubClass extends BaseClass function __construct () parent :: __construct ();
print "Конструктор класса SubClass\n" ;
>
>

class OtherSubClass extends BaseClass // inherits BaseClass's constructor
>

// In BaseClass constructor
$obj = new BaseClass ();

// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass ();

// In BaseClass constructor
$obj = new OtherSubClass ();
?>

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

В отличие от других методов, PHP не будет генерировать ошибку уровня E_STRICT , если __construct() будет перекрыт методом с другими параметрами, отличными от тех, которые находятся в родительском __construct().

Начиная с версии PHP 5.3.3, методы с именами, совпадающими с последним элементом имени класса, находящимся в пространстве имен, больше не будут считаться конструкторами. Это изменение не влияет на классы, не находящиеся в пространстве имен.

namespace Foo ;
class Bar public function Bar () // конструктор в версиях PHP 5.3.0-5.3.2
// обычный метод, начиная с версии PHP 5.3.3
>
>
?>

Деструкторы

PHP 5 предоставляет концепцию деструкторов, сходную с теми, что применяются в других ОО языках, таких, как C++. Деструктор будет вызван при освобождении всех ссылок на определенный объект или при завершении скрипта (порядок выполнения деструкторов не гарантируется).

class MyDestructableClass function __construct () print "Конструктор\n" ;
$this -> name = "MyDestructableClass" ;
>

function __destruct () print "Уничтожается " . $this -> name . "\n" ;
>
>

$obj = new MyDestructableClass ();
?>

Как и в случае с конструкторами, деструкторы, объявленные в родительском классе, не будут вызваны автоматически. Для вызова деструктора, объявленном в классе-родителе, следует обратиться к методу parent::__destruct() в теле деструктора-потомка. Также класс-потомок может унаследовать деструктор из родительского класса, если он не определен в нем.

Деструктор будет вызван даже в том случае, если скрипт был остановлен с помощью функции exit() . Вызов exit() в деструкторе предотвратит запуск всех последующих функций завершения.

Замечание:

Попытка бросить исключение в деструкторе (вызванного во время завершения скрипта) влечет за собой фатальную ошибку.

Читайте также: