В PHP возможно ли создать экземпляр класса без вызова конструктора класса? - PullRequest
16 голосов
/ 31 марта 2010

Можно ли каким-либо образом создать экземпляр класса php, не вызывая его конструктор?

У меня есть класс A, и при создании его экземпляра я передаю файл, а в конструкторе класса A открываю файл.

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

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

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

Ответы [ 5 ]

23 голосов
/ 10 декабря 2011

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

Вы можете использовать ReflectionClass и его метод newInstanceWithoutConstructor , представленный в PHP 5.4 Тогда очень легко создать экземпляр класса без вызова его конструктора:

$reflection = new ReflectionClass("ClassName");
$instance = $reflection->newInstanceWithoutConstructor(); //That's it!
13 голосов
/ 01 апреля 2010

Примечание: решение ниже для PHP 5.3 и ниже. Начиная с PHP 5.4, вы также можете сделать это с помощью Reflection, как показано в другом месте на этой странице .

Это действительно возможно.

Изменено с PHPUnit_Framework_MockObject_Generator

1  $myClass = unserialize(
2      sprintf(
3          'O:%d:"%s":0:{}',
4          strlen('MyClass'), 'MyClass'
5      )
6  );

Пожалуйста, имейте в виду, что такой код хорош и оправдан в такой среде, как PHPUnit. Но если вам нужно иметь такой код в вашем производственном коде, вы, вероятно, делаете что-то очень странное.


Так как вы попросили объяснения:

Когда вы сериализуете Объект , вы получаете строковое представление объекта. Например

echo serialize(new StdClass) // gives O:8:"stdClass":0:{}

O означает объект. 8 - длина строки имени класса. "stdClass", очевидно, имя класса. У сериализованного объекта установлено 0 свойств (на больше, чем позже ), обозначенных пустыми фигурными скобками. : - это просто разделители.

Каждая сериализованная строка может быть воссоздана в исходное «живое» значение с помощью функции unserialize . Делая так, обойдет конструктор. Как правильно заметил Чарльз, магический метод __wakeup() будет вызываться, если он определен (точно так же, как __sleep() будет вызываться при сериализации).

В строке 3 вы видите строку, подготовленную для использования с sprintf (строка 2). Как видите, длина строки имени класса задается как %d, а имя класса - как %s. Это говорит sprintf, что он должен использовать первый аргумент, переданный ему в строке 4, как цифру, а второй - как строку. Следовательно, результат вызова sprintf равен

'O:7:"MyClass":0:{}'

Вы должны заменить оба вхождения «MyClass» в строке 4 на желаемое имя класса, чтобы создать сериализованную строку класса, экземпляр которого вы хотите создать, не вызывая контроллер.

Эта строка затем не сериализуется в экземпляр MyClass в строке 1, минуя конструктор. Несериализованный экземпляр будет иметь все методы своего класса, а также любые свойства. Если в MyClass есть свойства, они будут иметь значения по умолчанию, если вы не добавите другие значения в сериализованную фиктивную строку.

И это уже все. Ничего особенного в этом нет.

12 голосов
/ 31 марта 2010

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

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

class MyClass {
    public __construct($file = null) {
        if ($file) {
            // perform whatever actions need to be done when $file IS set
        } else {
            // perform whatever actions need to be done when $file IS NOT set
        }
        // perform whatever actions need to be done regardless of $file being set
    }
}

Другой вариант - расширить класс таким образом, чтобы конструктор дочернего класса не вызывал конструктор родительского класса.

class MyParentClass {
    public __construct($file) {
        // perform whatever actions need to be done regardless of $file being set
    }
}

class MyChildClass extends MyParentClass {
    public __construct() {
        // perform whatever actions need to be done when $file IS NOT set
    }
}
2 голосов
/ 31 марта 2010

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


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

class A{
    public function __construct($arg1){
    }

    public static function foo(){
        //do something that doesn't involve the class properties
    }
}

Затем он может быть вызван без создания класса

//the constructor will not be called as we haven't created an instance of A
A::foo();

Разница между статической и нестатической функциями заключается в том, что статическая функция не может получить доступ к свойствам класса функций, которые также являются статическими. Так что если в foo() у вас есть код, который использует $this->, вы не можете сделать его статичным.

0 голосов
/ 31 марта 2010

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

в коде это будет выглядеть так:

class A {
  public static function func($txt) {
    print($txt);
  }
}

A::func('test');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...