Тип литья для определенных пользователем объектов

Как и в случае с __ToString, существует ли способ определить метод кастинга?

$obj = (MyClass) $another_class_obj; 

Нет необходимости вводить листинг в php.


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

В таких языках, как Java, есть две вещи, которые могут нести тип. У компилятора есть понятие о типе, а время выполнения имеет другое представление о типах. Типы компиляторов привязаны к переменным, тогда как механизм времени выполнения отслеживает тип значений (которые назначаются переменным). Типы переменных известны во время компиляции, тогда как типы значений известны только во время выполнения.

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

 class Alpha {} class Beta extends Alpha { public void sayHello() { System.out.println("Hello"); } } 

Если мы сейчас это сделаем:

 Alpha a = new Beta(); 

мы были бы в порядке, так как Beta является подклассом Alpha и, следовательно, допустимым значением для переменной a типа Alpha . Однако, если мы продолжим:

 a.sayHello(); 

Компилятор даст ошибку, так как метод sayHello не является допустимым методом для Alpha Независимо от того, что мы знаем, что a на самом деле является Beta .

Введите тип литья:

 ((Beta) a).sayHello(); 

Здесь мы сообщаем компилятору, что переменная a должна – в этом случае – рассматриваться как Beta . Это известно как литье типа. Эта лазейка очень полезна, потому что она допускает полиморфизм на языке, но, очевидно, это также задняя дверь для всех видов нарушений системы типов. Поэтому для обеспечения безопасности определенного типа существуют некоторые ограничения; Вы можете использовать только типы, связанные с ними. Например. вверх или вниз по иерархии. Другими словами, вы не сможете бросить в совершенно несвязанный класс Charlie .

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

 class Alpha {} class Beta extends Alpha { public void sayHello() { System.out.println("Hello"); } } class Charlie extends Alpha {} Alpha a = new Charlie(); ((Beta) a).sayHello(); 

Вышеприведенный код действителен для компилятора, но во время выполнения вы получите исключение, так как приведение из Beta в Charlie несовместимо.

Тем временем, на PHP-ферме.

Для PHP-компилятора справедливо следующее: он с радостью превратит это в исполняемый байтовый код, но вы получите ошибку времени выполнения:

 class Alpha {} class Beta extends Alpha { function sayHello() { print "Hello"; } } $a = new Alpha(); $a->sayHello(); 

Это связано с тем, что переменные PHP не имеют типа. Компилятор не имеет представления о том, какие типы времени выполнения действительны для переменной, поэтому он не пытается обеспечить ее соблюдение. Вы не указываете тип явно так же, как и на Java. Есть подсказки типов, да, но это просто контракты времени выполнения. До сих пор остается в силе следующее:

 // reuse the classes from above function tellToSayHello(Alpha $a) { $a->sayHello(); } tellToSayHello(new Beta()); 

Хотя переменные PHP не имеют типов, значения все еще сохраняются. Особый интересный аспект PHP заключается в том, что можно изменить тип значения. Например:

 // The variable $foo holds a value with the type of string $foo = "42"; echo gettype($foo); // Yields "string" // Here we change the type from string -> integer settype($foo, "integer"); echo gettype($foo); // Yields "integer" 

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

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

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

 // The variable $foo holds a value with the type of string $foo = "42"; echo gettype($foo); // Yields "string" // Here we change the type from string -> integer $bar = (integer) $foo; echo gettype($bar); // Yields "integer" 

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

Подводя итог: Тип casting – это операция, которая изменяет тип переменной (а не значение). Поскольку переменные не имеют типа в PHP, это не только невозможно сделать, но и бессмысленно спрашивать в первую очередь.

Вот функция для изменения класса объекта:

 /** * Change the class of an object * * @param object $obj * @param string $class_type * @author toma at smartsemantics dot com * @see http://www.php.net/manual/en/language.types.type-juggling.php#50791 */ function changeClass(&$obj,$new_class) { if(class_exists($class_type,true)) { $obj = unserialize(preg_replace("/^O:[0-9]+:\"[^\"]+\":/i", "O:".strlen($class_type).":\"".$new_class."\":", serialize($obj))); } } 

Если это не ясно, это не моя функция, она была взята из сообщения «toma at smartsemantics dot com» на http://www.php.net/manual/en/language.types.type-juggling.php # 50791

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

просто

 //Example of a sub class class YourObject extends MyObject { public function __construct(MyObject $object) { foreach($object as $property => $value) { $this->$property = $value; } } } $my_object = new MyObject(); $your_object = new YourObject($my_object); 

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

продвинутый

 //Class to return standard objects class Factory { public static function getObject() { $object = new MyObject(); return $object; } } //Class to return different object depending on the type property class SubFactory extends Factory { public static function getObject() { $object = parent::getObject(); switch($object->type) { case 'yours': $object = new YourObject($object); break; case 'ours': $object = new OurObject($object); break; } return $object; } } //Example of a sub class class YourObject extends MyObject { public function __construct(MyObject $object) { foreach($object as $property => $value) { $this->$property = $value; } } } 

Это не кастинг типов, но он делает то, что вам нужно.

Я переработал функцию Josh (которая будет ошибкой из-за неопределенной переменной $ new_class). Вот что я получил:

 function changeClass(&$obj, $newClass) { $obj = unserialize(preg_replace // change object into type $new_class ( "/^O:[0-9]+:\"[^\"]+\":/i", "O:".strlen($newClass).":\"".$newClass."\":", serialize($obj) )); } function classCast_callMethod(&$obj, $newClass, $methodName, $methodArgs=array()) { $oldClass = get_class($obj); changeClass($obj, $newClass); // get result of method call $result = call_user_func_array(array($obj, $methodName), $methodArgs); changeClass(&$obj, $oldClass); // change back return $result; } 

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

Boo для всех толчков, которые говорят, что «php не бросает» или «вам не нужно вводить php». Bullhockey. Кастинг – важная часть объектно-ориентированной жизни, и я бы хотел найти лучший способ сделать это, чем уродливые хаки сериализации.

Так что спасибо Джошу!

Я думаю, вам нужно набирать текст, чтобы сделать лучшую среду IDE. Но php сам язык не нуждается в кастомном типе, однако он поддерживает изменение типа среды выполнения в значениях переменных. Взгляните на автобоксинг и распаковку. Это то, что php по своей сути делает. Так жаль, что не лучше, чем уже IDE.

Если кастинг для типа намека – это все, что вам нужно, это работает.

 if( is_object($dum_class_u_want) && $dum_class_u_want instance of ClassYouWant ) { //type hints working now $dum_class_u_want ->is_smart_now(); } 

Ага.

Я не верю, что в PHP есть оператор перегрузки, чтобы справиться с этим:

 <?php class MyClass { protected $_number; static public function castFrom($obj) { $new = new self(); if (is_int($obj)) { $new->_number = $obj; } else if ($obj instanceOf MyNumberClass){ /// some other type of casting } return $new; } } $test = MyClass::castFrom(123123); var_dump($test); 

Это один из способов справиться с этим.