Подтвердить что ты не робот

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

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

$obj = (MyClass) $another_class_obj;
4b9b3361

Ответ 1

Нет необходимости вводить текст в 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, это не только невозможно сделать, но и бессмысленно задавать в первую очередь.

Ответ 2

Хотя нет необходимости вводить текст в 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;
        }
    }
}

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

Ответ 3

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

/**
 * 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

Ответ 4

Я переработал функцию Josh (которая будет ошибкой из-за переменной undefined $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 does not cast" или "вам не нужно вводить php". Bullhockey. Кастинг - важная часть объектно-ориентированной жизни, и я бы хотел найти лучший способ сделать это, чем уродливые хаки сериализации.

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

Ответ 5

Я не верю, что в 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);

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

Ответ 6

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

Ответ 7

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

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

Ага.

Ответ 8

Я думаю, что вы имеете в виду Type-Hinting.

Начиная с PHP 7.2, вы можете указывать аргументы подсказки в функциях:

function something(Some_Object $argument) {...} # Type-hinting object on function arguments works on PHP 7.2+

Но вы не можете напечатать это так:

(Some_Object) $variable = get_some_object($id); # This does not work, even in PHP 7.2

Альтернатива для объектов с хинтингом типов, хотя она официально не реализована в PHP:

$variable = get_some_object($id); # We expect Some_Object to return
is_a($argument, 'Some_Object') || die('get_some_object() function didn't return Some_Object');

Ответ 9

Даже на PHP 7.2, если вы попробуете простое приведение типов, вот так

class One
{
    protected $one = 'one';
}

class Two extends One
{
    public function funcOne()
    {
        echo $this->one;
    }
}


    $foo = new One();
    $foo = (Two) $foo;
    $foo->funcOne();

Вы получите ошибку, как это

Ошибка синтаксического разбора PHP: синтаксическая ошибка, неожиданное '$ foo' (T_VARIABLE) в xxx.php в строке xxx

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

Вы можете сделать это с помощью Wrapper

class One
{
    protected $one;
    public function __construct(string $one)
    {
        $this->one = $one;
    }
    public function pub(string $par){
        echo (__CLASS__ . ' - ' . __FUNCTION__ . ' - ' . $this->one . '-' .$par);
    }
}

class Wrapper
{
    private $obj;

    public function __construct(One $obj)
    {
        $this->obj = $obj;
    }
    public function newFunction()
    {
        echo (__CLASS__ . ' - ' . __FUNCTION__);
    }
    public function __call($name, $arguments)
    {
        return call_user_func_array([$this->obj, $name], $arguments);
    }
}

    $foo = new One('one');
    $foo->pub('par1');
    $foo = new Wrapper($foo);
    $foo->pub('par2');
    $foo->newFunction();

Один - паб - один пар

Один - паб - один-пар2

Обертка - новая функция

Что делать, если вы хотите получить защищенную собственность?

Вы тоже можете это сделать

class One
{
    protected $one;

    public function __construct(string $one)
    {
        $this->one = $one;
    }
}


    $foo = new One('one');
    $tmp = (new class ($foo) extends One {
            protected $obj;
            public function __construct(One $obj)
            {
                $this->obj = $obj;
                parent::__construct('two');
            }
            public function getProtectedOut()
            {
                return $this->obj->one;
            }
        } )->getProtectedOut();

    echo ($tmp);

Ты получишь

один

И вы можете получить get_protected_in таким же образом

Ответ 10

В основном мне нужно приведение типов для включения intellisense - поэтому я просто создаю помощник приведения типов:

function castToTest($val): Test
{
    return $val;
}
$test = castToTest(require("someFile.php"));

Возвращение из файла выглядит некрасиво и не позволяет использовать подсказки типа, так что это прекрасный пример того, как вы можете достичь intellisense с помощью помощника приведения типов.