就像我们使用__ToString一样,有没有办法定义一个方法来进行投射?
$obj = (MyClass) $another_class_obj;
答案 0 :(得分:99)
无需在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-farm。
以下内容对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"
从语法上讲,这看起来很像用静态类型语言编写类型转换器。因此,它经常与类型转换混淆,即使它仍然是运行时类型转换。
总结:类型转换是一种更改变量类型的操作(不是值)。由于变量在PHP中没有类型,因此不仅无法做到,而且首先要问一个荒谬的事情。
答案 1 :(得分: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);
所以你要做的就是将父对象传递给子对象的构造函数,然后让构造函数复制属性。您甚至可以根据需要过滤/更改它们。
//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;
}
}
}
它不是类型转换,但它可以满足您的需求。
答案 2 :(得分: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)));
}
}
如果不清楚,这不是我的功能,它来自http://www.php.net/manual/en/language.types.type-juggling.php#50791上的“toma at smartsemantics dot com”的帖子
答案 3 :(得分: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;
}
它就像你期望一个类演员一样工作。你可以为访问班级成员建立类似的东西 - 但我认为我不会需要它,所以我会留给其他人。
对所有说“php不会投射”或“你不需要在php中投射”的混蛋嘘声。 Bullhockey。 Casting是面向对象生活的重要组成部分,我希望我能找到比丑陋的序列化黑客更好的方法。
谢谢Josh!
答案 4 :(得分:1)
我不相信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);
是处理它的一种方法。
答案 5 :(得分:1)
我认为您需要输入强制转换才能创建更好的IDE。但是php语言本身并不需要类型转换,但它支持运行时类型更改变量中的值。看看自动装箱和拆箱。这就是php固有的功能。很遗憾没有比IDE更好的了。
答案 6 :(得分:1)
如果您正在进行类型提示的转换,则可以使用。
if( is_object($dum_class_u_want) && $dum_class_u_want instanceof ClassYouWant )
{
// type hints working now
$dum_class_u_want->is_smart_now();
}
是的。
答案 7 :(得分:0)
我认为您的意思是类型提示。
从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');
答案 8 :(得分:0)
即使在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解析错误:语法错误,第xxx行上xxx.php中出现意外的'$ foo'(T_VARIABLE)
所以您基本上不能做到这一点,但是再想一想,也许您只想在该类的其他公共功能之上只需要一个新功能?
您可以使用包装器
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();
一个-客栈-one-par1
一个-客栈-one-par2
包装器-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);
你会得到
一个
您可以用相同的方式获取保护
答案 9 :(得分:0)
我主要需要类型转换才能启用智能感知-所以我只创建一个类型转换助手:
function castToTest($val): Test
{
return $val;
}
$test = castToTest(require("someFile.php"));
从文件返回很丑陋,并且不允许类型提示,因此这是如何使用类型强制转换助手实现智能感知的完美示例。
答案 10 :(得分:0)
您可以创建两个反射对象,然后将值复制到其他示例,可以在https://github.com/tarikflz/phpCast
中找到scipy.special.expn
答案 11 :(得分:0)
被选为正确的答案实际上是错误的答案(或者,问题被误解了)。
这个问题的正确解释与面向对象设计范式的基本原则有关,根据该原则,子类应该能够分配给其父类的变量,或者分配给它的接口的变量由类实现。
因为这是面向对象设计的基础,不能说A语言需要,B语言不需要。
正确答案是,迄今为止,PHP 缺乏面向对象设计的基本要求。
为了使代码符合 A) 依赖倒置原则 (DIP),以及 B) 接口隔离原则(S.O.L.I.D 中的“I”)
... PHP 必须允许使用具体类实现的接口名称对变量进行类型提示,以便只能使用实现(服务)类的特定于接口的功能。
这是面向对象设计的基本原则,如果不能声明特定类型的接口代码的变量的类型,就不能符合(也不能享受)DIP和接口隔离原则。
>另外,动态绑定总是在运行时完成,而不是在编译时完成,因此 PHP 在提供 OO 范式的基本元素时应该没有问题。