为用户定义的对象键入转换

Ars*_*ham 51 php casting

就像我们使用__ToString一样,有没有办法定义一个方法来进行投射?

$obj = (MyClass) $another_class_obj;
Run Code Online (Sandbox Code Playgroud)

tro*_*skn 100

没有必要在php中输入强制转换.


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

在诸如Java之类的语言中,有两种东西可以携带类型.编译器有关于类型的概念,运行时对类型有另一种想法.编译器类型与变量相关联,而运行时引擎跟踪值的类型(分配给变量).变量类型在编译时是已知的,而值类型仅在运行时已知.

如果输入代码违反了编译器类型系统,编译器将禁止并停止编译.换句话说,编译违反静态类型系统的代码是不可能的.这会捕获某类错误.例如,采用以下(简化的)Java代码:

class Alpha {}

class Beta extends Alpha {
  public void sayHello() {
    System.out.println("Hello");
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我们现在这样做:

Alpha a = new Beta();
Run Code Online (Sandbox Code Playgroud)

我们会没事的,因为Beta它是类Alpha的变量a的有效值,因此是类的变量的有效值Alpha.但是,如果我们继续这样做:

a.sayHello();
Run Code Online (Sandbox Code Playgroud)

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

输入类型转换:

((Beta) a).sayHello();
Run Code Online (Sandbox Code Playgroud)

在这里,我们告诉编译器变量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();
Run Code Online (Sandbox Code Playgroud)

上面的代码对编译器有效,但在运行时,你会得到一个异常,因为转换为Betato Charlie是不兼容的.

同时,回到PHP农场.

以下内容对PHP编译器有效 - 它很乐意将其转换为可执行字节代码,但是会出现运行时错误:

class Alpha {}

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

这是因为PHP变量没有类型.编译器不知道哪个运行时类型对变量有效,因此它不会尝试强制执行它.您也没有像在Java中那样明确指定类型.有类型提示,是的,但这些只是运行时合同.以下内容仍然有效:

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

尽管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"
Run Code Online (Sandbox Code Playgroud)

这个功能有时与类型转换相混淆,但这是用词不当.类型仍然是值的属性,类型更改在运行时发生 - 而不是在编译时.

在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"
Run Code Online (Sandbox Code Playgroud)

从语法上来说,这看起来很像用静态类型语言编写类型转换器.因此,它经常与类型转换混淆,即使它仍然是运行时类型转换.

总结一下:类型转换是一种改变变量类型(而不是值)的操作.由于变量在PHP中没有类型,因此不仅无法做到,而且首先要问的是无意义的事情.

  • 那么,不要把它留给自己. (3认同)
  • 这似乎与PHP手册相矛盾 (3认同)
  • PHP中的变量是无类型的; 只有它们引用的对象才有类型.因此你*不能*在php中转换变量. (2认同)
  • 这是一个很好的答案.我觉得你可以通过提及在PHP值中有类型来改进它.这很有趣,因为值可以更改其类型 - 可以告诉Alpha对象"成为Beta对象".例如,Python和Javascript支持这一点(在Python中通过更改其`__class__`字段).但是,PHP没有,并且具有静态类层次结构.因此,尽管允许PHP值更改其类型是有意义的,但语言中不允许这样做. (2认同)

Jrg*_*gns 8

虽然不需要在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);
Run Code Online (Sandbox Code Playgroud)

所以你要做的就是将父对象传递给子对象的构造函数,然后让构造函数复制属性.您甚至可以根据需要过滤/更改它们.

高级

//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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它不是类型转换,但它可以满足您的需求.


Jos*_*osh 7

这是一个更改对象类的函数:

/**
 * 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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

如果不清楚,这不是我的功能,它来自http://www.php.net/manual/en/language.types.type-juggling.php上的"toma at smartsemantics dot com" . #50791

  • 我同意,我的函数改变了对象的类.问题是作者的意图.根据PHP手册,变量*do*有类型和类型转换是可能的:http://us.php.net/manual/en/language.types.type-juggling.php#language.types.typecasting (4认同)
  • 这不是一个类型.那就是改变对象的类型 - 而不是变量.在PHP中,您不能进行类型转换,因为变量没有类型. (3认同)
  • 为什么这么多投票?toma的原始函数有一个错误(他错过了preg_replace之后的((`)),但是这个甚至无法正常工作。您使用了从未定义的变量$ class_type,也许我错过了一些东西但是从我的角度来看,这似乎使错误的代码更加糟糕。 (2认同)

B T*_*B T 7

我重写了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;
}
Run Code Online (Sandbox Code Playgroud)

它就像你期望一个类演员一样工作.您可以为访问类成员构建类似的东西 - 但我认为我不会需要它,所以我会留给其他人.

嘘说所有的混蛋,说"PHP不投"或"你不需要在php中投射".Bullhockey.Casting是面向对象生活的重要组成部分,我希望我能找到比丑陋的序列化黑客更好的方法.

谢谢Josh!