Конструкторы

Существует небольшое, но очень важное различие между конструкторами и деструкторами в C ++ и методами __construct () и __destruct () в PHP.

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

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

<?php

    // базовый класс в PHP, в котором вызывается абстрактный метод
    abstract class BASE 
    {
        // конструктор
        public function __construct() 
        {
            // метод абстрактного вызова
            $this->doSomething();
        }

        // абстрактный метод, реализуемый производными классами
        public abstract function doSomething();
    }

    // производный класс
    class DERIVED extends BASE 
    {
        // реализовать абстрактный метод
        public function doSomething() 
        {
            echo("doSomething()\n");
        }
    }

    // создать экземпляр производного класса
    $d = new DERIVED();
?>  

Этот скрипт выводит «doSomething ()». Причиной этого является то, что __construct () вообще не является конструктором, но является очень обычным методом, который просто является первым вызванным методом и который вызывается автоматически после создания объекта.

Эта разница важна для вас как программиста на C ++, потому что вы никогда не должны путать свой конструктор C ++ с методом PHP __construct (). В конструкторе C ++ объект строится, и не все данные еще доступны. Виртуальные методы не могут быть вызваны, и объект также еще не существует в пользовательском пространстве PHP.

После того, как конструктор закончен, PHP-механизм берет управление и создает объект PHP, а библиотека PHP-CPP связывает этот объект PHP с вашим объектом C ++. Только после того, как объект PHP и объект C ++ будут полностью построены, метод __construct () вызывается - как обычный метод. Поэтому не редкость иметь как конструктор C ++, так и метод __construct () в вашем классе. Конструктор C ++ для инициализации переменных-членов и __construct () для активации объекта.

#include <phpcpp.h>

/**
 *  Простой класс счетчика
 */
class Counter : public Php::Base
{
private:
    /**
     *  Внутреннее значение
     *  @var int
     */
    int _value = 0;

public:
    /**
     *  конструктор c ++
     */
    Counter() = default;

    /**
     *  c ++ деструктор
     */
    virtual ~Counter() = default;

    /**
     *  php "конструктор"
     *  @param  params
     */
    void __construct(Php::Parameters &params)
    {
        // скопировать первый параметр (если имеется)
        if (!params.empty()) _value = params[0];
    }

    /**
     *  функции для увеличения и уменьшения
     */
    Php::Value increment() { return ++_value; }
    Php::Value decrement() { return --_value; }
    Php::Value value() const { return _value; }
};

/**
 *  Переключитесь в контекст C, чтобы функция get_module ()
 *   могла быть вызвана C-программами (с помощью механизма Zend)
 */
extern "C" {
    /**
     *  Функция запуска для расширения
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");

        // описание класса, чтобы PHP знал, какие методы доступны
        Php::Class<Counter> counter("Counter");
        counter.method<&Counter::__construct>("__construct");
        counter.method<&Counter::increment>("increment");
        counter.method<&Counter::decrement>("decrement");
        counter.method<&Counter::value>("value");

        // добавить класс к расширению
        myExtension.add(std::move(counter));

        // вернуть расширение
        return myExtension;
    }
}

Вышеприведенный код показывает, что __construct () зарегистрирован, как если бы он был обычным методом - и это то, что он есть. Пример, который мы использовали ранее (тот, у кого есть класс Counter), теперь расширен, так что можно дать ему начальное значение счетчику, передав значение «конструктору».

<?php
    $counter = new Counter(10);
    $counter->increment();
    echo($counter->value()."\n");
?>

Поскольку метод __construct () рассматривается как обычный метод, вы также можете указать его параметры и является ли этот метод общедоступным, приватным или защищенным. __construct () также непосредственно вызывается из пространства пользователя PHP, поэтому производные методы могут явно вызывать parent :: __ construct ().

Частные конструкторы

Как и любой другой метод, метод __construct () также может быть помечен как private или protected. Если вы сделаете это, вы сделаете невозможным создание экземпляров вашего класса из PHP-скриптов. Важно понимать, что конструктор C ++ и деструктор C ++ по-прежнему вызываются в таких ситуациях, потому что это вызов __construct (), который будет терпеть неудачу, а не фактическую конструкцию объекта.

Да, действительно: если вы сделаете метод __construct () private, а внутри скрипта PHP выполняется вызов «new Counter ()», библиотека PHP-CPP сначала создаст экземпляр нового экземпляра вашего класса, а затем сообщит об ошибке, поскольку Метод __construct () является private, а затем сразу же уничтожает объект (и вызывает деструктор C ++).

#include <phpcpp.h>

/**
 *  Переключитесь в контекст C, чтобы функция get_module ()
 *  могла быть вызвана C-программами (с помощью механизма Zend)
 */
extern "C" {
    /**
     *  Начальная точка расширения
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");

        // описание класса, чтобы PHP знал, какие методы доступны
        Php::Class<Counter> counter("Counter");

        // добавьте к классу частный метод __construct, чтобы объекты не могли 
        // быть построены из PHP-скриптов. Имейте в виду, что конструктор C ++
        // вызывает вызов - это будет вызов первой функции __construct (),
        // которая завершится неудачно, а не фактическая конструкция объекта.
        counter.method<&Counter::__construct>("__construct", Php::Private);

        ...
    }
}

Клонирование объектов

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

#include <phpcpp.h>

/**
 *  Простой класс счетчика
 */
class Counter : public Php::Base
{
private:
    /**
     *  Internal value
     *  @var int
     */
    int _value = 0;

public:
    /**
     *  конструктор c ++
     */
    Counter() = default;

    /**
     *  Удалить конструктор копирования
     *  
     *  Удалив конструктор копирования, оператор клонирования PHP
     *  будет автоматически деактивирован. PHP будет вызывать ошибку,
     *  если объект пытается клонировать.
     *  
     *  @param  counter
     */
    Counter(const Counter &counter) = delete;

    /**
     *  деструктор c ++ 
     */
    virtual ~Counter() = default;

    /**
     *  php "конструктор"
     *  @param  params
     */
    void __construct(Php::Parameters &params)
    {
        // скопировать первый параметр (если имеется)
        if (!params.empty()) _value = params[0];
    }

    /**
     *  функции для увеличения и уменьшения
     */
    Php::Value increment() { return ++_value; }
    Php::Value decrement() { return --_value; }
    Php::Value value() const { return _value; }
};

/**
 *  Переключитесь в контекст C, чтобы функция get_module ()
 *  могла быть вызвана C-программами (с помощью механизма Zend)
 */
extern "C" {
    /**
     *  Функция запуска для расширения
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");

        // описание класса, чтобы PHP знал, какие методы доступны
        Php::Class<Counter> counter("Counter");
        counter.method<&Counter::__construct>("__construct");
        counter.method<&Counter::increment>("increment");
        counter.method<&Counter::decrement>("decrement");
        counter.method<&Counter::value>("value");

        // альтернативный способ сделать объект непригодным
        counter.method("__clone", Php::Private);

        // добавить класс к расширению
        myExtension.add(std::move(counter));

        // вернуть расширение
        return myExtension;
    }
}

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

Построение объектов

Класс Php::Value может использоваться как регулярная $переменная PHP, и поэтому вы можете использовать ее для хранения экземпляров объекта. Но как вы создаете новые объекты? Для этого у нас есть класс Php::Object, который является просто переопределенным классом Php::Value с альтернативными конструкторами и некоторыми дополнительными проверками, чтобы предотвратить использование объекта Php::Object для хранения значений, отличных от объектов.

// новая переменная, удерживающая строку "Counter"
Php::Value counter0("Counter");

// новая переменная, удерживающая вновь созданный объект типа "Counter",
// __construct () вызывается без параметров
Php::Object counter1("Counter");

// новая переменная, удерживающая вновь созданный объект, и 
// __construct () вызывается со значением 10
Php::Object counter2("Counter", 10);

// новый встроенный объект DateTime, построенный с помощью "now"
Php::Object time("DateTime", "now");

// valid, Php :: Object - расширенное значение Php :: Value и 
// может быть присвоен базовому объекту Php :: Value
Php::Value copy1 = counter1;

// недопустимый оператор, Php:: Object может использоваться только для хранения объектов
Php::Object copy2 = counter0;

Конструктор объекта Php::Object принимает имя класса и необязательный список параметров, которые будут переданы функции __construct (). Вы можете использовать имена из встроенных классов PHP и других расширений (например, DateTime), классов из вашего расширения (например, Counter) и даже классов из пользовательского пространства PHP.

Класс Php::Object также может использоваться, если вы хотите создать экземпляр собственного C ++-класса без вызова функции __construct (). Это может быть полезно, например, когда метод __construct () является закрытым или когда вы хотите обойти вызов с помощью собственного метода __construct ().

#include <phpcpp.h>

// фактическая реализация класса
class Counter : public Php::Base
{
private:
    int _value = 0;

public:
    // конструктор c ++
    Counter(int value) : _value(value) {}

    // деструктор c ++ 
    virtual ~Counter() = default;

    // php "конструктор"
    void __construct() {}

    // функции для увеличения и уменьшения
    Php::Value value() const { return _value; }
};

// для создания нового таймера
Php::Value createTimer()
{
    return Php::Object("Counter", new Counter(100));
}

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");

        // описание класса, чтобы PHP знал, какие методы доступны,
        // метод __construct является закрытым, поскольку скриптам PHP
        // не разрешено создавать экземпляры Counter
        Php::Class<Counter> counter("Counter");
        counter.method<&Counter::__construct>("__construct", Php::Private);
        counter.method<&Counter::value>("value");

        // добавить класс к расширению
        myExtension.add(std::move(counter));

        // добавьте заводскую функцию для создания таймера на расширение
        myExtension.add("createTimer", createTimer);

        // вернуть расширение
        return myExtension;
    }
}

В приведенном выше коде мы создали функцию __construct () класса Counter. Это делает невозможным создание экземпляров этого класса - как из PHP-скриптов пользователя, так и через вызовы Php::Object("Counter"), потому что при построении объектов этими способами в конечном итоге будет создан запрещенный вызов __construct ().

У Php :: Object есть альтернативный синтаксис, который берет указатель на класс C ++ (выделенный в куче, с оператором new!) И который превращает этот указатель в переменную PHP без вызова метода __construct (). Обратите внимание, что вы также должны указать имя класса, потому что классы C ++ не содержат никакой информации о себе (например, их имя), тогда как в PHP такая информация требуется для обработки отражений и функций, таких как get_class ().

Далее.