Php 用户定义对象的类型转换

Php 用户定义对象的类型转换,php,casting,Php,Casting,就像我们对uuu ToString所做的那样,有没有一种方法可以定义一种用于强制转换的方法 $obj = (MyClass) $another_class_obj; 我不相信PHP中有重载操作符来处理这个问题,但是: <?php class MyClass { protected $_number; static public function castFrom($obj) { $new = new self(); if (is_int($obj)) {

就像我们对uuu ToString所做的那样,有没有一种方法可以定义一种用于强制转换的方法

$obj = (MyClass) $another_class_obj;

我不相信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);

不需要在php中键入cast


编辑:由于这个话题似乎会引起一些混乱,我想我应该详细说明一下

在Java这样的语言中,有两种东西可以携带类型。编译器对类型有一个概念,而运行时对类型有另一个概念。编译器类型与变量绑定,而运行时引擎跟踪值的类型(分配给变量)。变量类型在编译时已知,而值类型仅在运行时已知

如果一段输入代码违反编译器类型系统,编译器将呕吐并停止编译。换句话说,不可能编译一段违反静态类型系统的代码。这捕获了某类错误。例如,以下面一段(简化的)Java代码为例:

class Alpha {}

class Beta extends Alpha {
  public void sayHello() {
    System.out.println("Hello");
  }
}
如果我们现在这样做:

Alpha a = new Beta();
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
Alpha
的子类,因此是
Alpha
类型的变量
a
的有效值。然而,如果我们继续这样做:

a.sayHello();
编译器会给出一个错误,因为方法
sayHello
不是
Alpha
的有效方法-不管我们知道
a
实际上是
Beta

输入类型转换:

((Beta) a).sayHello();
在这里,我们告诉编译器,变量
a
在本例中应被视为
Beta
。这被称为类型转换。这个漏洞非常有用,因为它允许语言中存在多态性,但显然它也是各种类型系统违规的后门。为了保持某些类型的安全性,因此存在一些限制;只能强制转换到相关的类型。在等级制度上或下。换句话说,您将无法强制转换到完全不相关的类
Charlie

需要注意的是,所有这些都发生在编译器中,也就是说,它发生在代码运行之前。Java仍然可以进入运行时类型错误。例如,如果您这样做:

Alpha a = new Beta();
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"
(Some_Object) $variable = get_some_object($id); # This does not work, even in PHP 7.2
从语法上看,这很像用静态类型语言编写的类型转换。因此,它也经常与类型转换混淆,即使它仍然是运行时类型转换


总而言之:类型转换是一种更改变量类型(而不是值)的操作。因为变量在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.com”在

上的一篇文章中获取的。我重新编写了Josh posted函数(由于未定义的$new_类变量,该函数将出错)。以下是我得到的:

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;
}
它的工作原理就像你期望一个班级演员的工作原理一样。您可以构建类似的东西来访问类成员,但我认为我永远都不需要它,所以我将把它留给其他人

嘘所有说“php不强制转换”或“你不需要在php中强制转换”的混蛋。曲棍球。强制转换是面向对象生活的一个重要部分,我希望我能找到一种比丑陋的序列化黑客更好的方法


谢谢你,乔希

我认为您需要键入cast来制作更好的IDE。但是php语言本身不需要类型转换,但是它支持对变量中的值进行运行时类型更改。看看自动装箱和取消装箱。这就是php固有的功能。因此,很抱歉,IDE并不比现在更好。

尽管不需要在PHP中键入cast,但您可能会遇到这样一种情况,即希望将父对象转换为子对象

简单的 因此,您所要做的就是将父对象传递给子对象的构造函数,并让构造函数复制属性。您甚至可以根据需要过滤/更改它们

先进的
这不是类型转换,但它可以满足您的需要。

如果您只需要为类型暗示进行转换,这就行了

if(is_object($dum_class______________)want)和&$dum_class__________________
{
//输入提示现在可以工作了
$dum_级_
(Some_Object) $variable = get_some_object($id); # This does not work, even in PHP 7.2
$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');
class One
{
    protected $one = 'one';
}

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


    $foo = new One();
    $foo = (Two) $foo;
    $foo->funcOne();
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();
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);
function castToTest($val): Test
{
    return $val;
}
$test = castToTest(require("someFile.php"));
    public static function cast($sourceObject)
{
    $destinationObject = new self();
    try {
        $reflectedSource = new ReflectionObject($sourceObject);
        $reflectedDestination = new ReflectionObject($destinationObject);
        $sourceProperties = $reflectedSource->getProperties();
        foreach ($sourceProperties as $sourceProperty) {
            $sourceProperty->setAccessible(true);
            $name = $sourceProperty->getName();
            $value = $sourceProperty->getValue($sourceObject);
            if ($reflectedDestination->hasProperty($name)) {
                $propDest = $reflectedDestination->getProperty($name);
                $propDest->setAccessible(true);
                $propDest->setValue($destinationObject, $value);
            } else {
                $destinationObject->$name = $value;
            }
        }
    } catch (ReflectionException $exception) {
        return $sourceObject;
    }
    return $destinationObject;
}