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

Обновлено: 27.04.2024

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

Именем класса может быть любое слово, при условии, что оно не входит в список зарезервированных слов PHP, начинается с буквы или символа подчеркивания и за которым следует любое количество букв, цифр или символов подчеркивания. Если задать эти правила в виде регулярного выражения, то получится следующее выражение: ^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$.

Класс может содержать собственные константы, переменные (называемые свойствами) и функции (называемые методами).

class SimpleClass
// объявление свойства
public $var = 'значение по умолчанию' ;

// объявление метода
public function displayVar () echo $this -> var ;
>
>
?>

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

class A
function foo ()
if (isset( $this )) echo '$this определена (' ;
echo get_class ( $this );
echo ")\n" ;
> else echo "\$this не определена.\n" ;
>
>
>

class B
function bar ()
// Замечание: следующая строка вызовет предупреждение, если включен параметр E_STRICT.
A :: foo ();
>
>

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

// Замечание: следующая строка вызовет предупреждение, если включен параметр E_STRICT.
A :: foo ();
$b = new B ();
$b -> bar ();

// Замечание: следующая строка вызовет предупреждение, если включен параметр E_STRICT.
B :: bar ();
?>

Результат выполнения данного примера:

Для создания экземпляра класса используется директива new. Новый объект всегда будет создан, за исключением случаев, когда он содержит конструктор, в котором определен вызов исключения в случае ошибки. Рекомендуется определять классы до создания их экземпляров (в некоторых случаях это обязательно).

Если с директивой new используется строка ( string ), содержащая имя класса, то будет создан новый экземпляр этого класса. Если имя находится в пространстве имен, то оно должно быть задано полностью.

// Это же можно сделать с помощью переменной:
$className = 'Foo' ;
$instance = new $className (); // Foo()
?>

В контексте класса можно создать новый объект через new self и new parent.

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

$instance = new SimpleClass ();

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

$instance -> var = '$assigned будет иметь это значение' ;

$instance = null ; // $instance и $reference становятся null

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

Результат выполнения данного примера:

В PHP 5.3.0 введены несколько новых методов создания экземпляров объекта:

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 );
?>

Результат выполнения данного примера:

extends

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

Наследуемые методы и свойства могут быть переопределены (за исключением случаев, когда метод класса объявлен как final) путем объявления их с теми же именами, как и в родительском классе. Существует возможность доступа к переопределенным методам или статическим методам путем обращения к ним через parent::

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

class ExtendClass extends SimpleClass
// Переопределение метода родителя
function displayVar ()
echo "Расширенный класс\n" ;
parent :: displayVar ();
>
>

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

Результат выполнения данного примера:

::class

Начиная с версии PHP 5.5 можно использовать ключевое слово class для разрешения имени класса. С помощью конструкции ClassName::class можно получить строку с абсолютным именем класса ClassName. Обычно это довольно полезно при работе с классами, использующими пространства имен.

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 позволяет использовать только один конструктор в классе, что довольно раздражительно. Вероятно, мы никогда не получим нормальную возможность перегрузки конструкторов в PHP, но кое-что сделать все же можно. Для примера возьмем простой класс, хранящий значение времени. Какой способ создания нового объекта лучше:


Правильным ответом будет «в зависимости от ситуации». Оба способа могут являются корректным с точки зрения полученного результата. Реализуем поддержку обоих способов:

Выглядит отвратительно. Кроме того поддержка класса будет затруднена. Что произойдет, если нам понадобится добавить еще несколько способов создания экземпляров класса Time?


Также, вероятно, стоит добавить поддержку числовых строк (защита от дурака не помешает):

Реорганизация кода с использованием именованных конструкторов

Добавим несколько статичных методов для инициализации Time. Это позволит нам избавиться от условий в коде (что зачастую является хорошей идеей).


Теперь каждый метод удовлетворяет принцип Единой ответственности. Публичный интерфейс прост и понятен. Вроде бы закончили? Меня по прежнему беспокоит конструктор, он использует внутреннее представление объекта, что затрудняет изменение интерфейса. Положим, по какой-то причине нам необходимо хранить объединенное значение времени в строковом формате, а не по отдельности, как раньше:


Это некрасиво: нам приходится разбивать строку, чтобы потом заново соединить её в конструкторе. А нужен ли нам конструктор для конструктора?

Мы встроили тебе конструктор в конструктор.

Нет, обойдемся без него. Реорганизуем работу методов, для работы с внутренним представлением напрямую, а конструктор сделаем приватным:

Единообразие языковых конструкций

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

  • fromString — использует в названии детали реализации PHP;
  • fromValues ​​- использует своего рода общий термин программирования;
  • fromMinutesSinceMidnight - использует обозначения из предметной области.
  • fromString => fromTime
  • fromValues => fromHoursAndMinutes


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

Часть 2: Когда использовать статические методы

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() в деструкторе предотвратит запуск всех последующих функций завершения.

Замечание:

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

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