【问题标题】:__toString magic and type coercion__toString 魔法和类型强制
【发布时间】:2011-07-08 10:13:56
【问题描述】:

我创建了一个 Template 类来管理视图及其相关数据。它实现了IteratorArrayAccess,并允许“子模板”以便于使用,如下所示:

<p><?php echo $template['foo']; ?></p>
<?php foreach($template->post as $post): ?>
    <p><?php echo $post['bar']; ?></p>
<?php endforeach; ?>

无论如何,与其使用内联核心函数,例如hash()date(),我认为创建一个名为TemplateData 的类会很有用,它可以充当包装器对于存储在模板中的任何数据。

这样,我可以添加一个常用的格式化方法列表,例如:

echo $template['foo']->asCase('upper');
echo $template['bar']->asDate('H:i:s');
//etc..

当在控制器中通过$template['foo'] = 'bar'; 设置值时,'bar' 的值存储在它自己的TemplateData 对象中。

我使用了魔法__toString(),所以当您回显TemplateData 对象时,它会转换为(string) 并转储它的值。然而,尽管控制器和视图不应该修改数据的口头禅,每当我做这样的事情时:

$template['foo'] = 1;
echo $template['foo'] + 1; //exception

它死在Object of class TemplateData could not be converted to int;除非我重铸 $template['foo'] 为字符串:

echo ((string) $template['foo']) + 1; //outputs 2

有点挫败了必须跳过那个箍的目的。 对于这种存在的行为,是否有任何解决方法,或者我应该就这样,偶然防止视图中的数据修改?

【问题讨论】:

    标签: php type-conversion tostring magic-methods


    【解决方案1】:

    我稍微改变了我的实现,它似乎暂时可以工作;我已将__invoke() 方法添加到TemplateData 类,该类返回$_data 属性,无需进行类型转换,保持不变。所以在实践中:

    //in controller
    $template['foo'] = 'bar';
    $template['abc'] = 123;
    
    //in view
    echo $template['foo']();                       //bar
    echo $template['foo']->asHash('md5');          //37b51d194a7513...
    echo $template['abc']();                       //123
    echo $template['abc']() + 123;                 //246
    echo $template['abc']->asHash('md5');          //202cb962ac5907...
    
    //__invoke also takes $function and $arguments for alternative syntax
    echo $template['foo']('asHash', array('md5')); //37b51d194a7513...
    

    我想自从我征服了这座山之后,我的下一步就是以某种方式允许链:

    echo $template['foo']->asHash('md5')->asCase('upper');
    

    然而,这似乎很难做到。跟踪“链状态”是我之前尝试过的,但没有成功。从某种意义上说,使用__invoke() 会相当容易(但很混乱):

    echo $template['foo'](
        array('asHash', 'asCase'),          //ordered array of method names
        array(array('md5'), array('upper')) //ordered array of method argument arrays
    );
    

    任何人都可以看到其中的任何问题,或替代方法的其他推理吗?

    【讨论】:

    • 欢迎大声思考。我不想接受我自己的答案。我敢肯定,外面有人在思考我被困在的任何盒子之外。
    【解决方案2】:

    PHP 不支持operator loading。如果您希望能够执行加法(或其他与运算符相关的操作),您将需要为您想要支持的每个运算符提供一个方法。很可能在基本模板类上实现它对于常见情况就足够了,如果需要的话,可以在以后对其进行扩展。虽然在对象上使用(string) 是一种快捷方式,但我建议不要这样做。

    【讨论】:

    • 谢谢 Kevin Peno; 是的,正如我提到的,我已经看到了支持运算符重载的扩展,以及补充魔法方法(例如__cast($type)),但是当然这些是扩展,我不能在生产中依赖它们。我想完全避免使用(string) 快​​捷方式。我现在在想,除了 TemplateData 对象之外,如果 Template 类使用 thetaiko 的建议保持类型(在 TemplateData 对象创建时确定),我可以实现TemplateoffsetGet() 方法中的一些类型转换功能...嗯...
    【解决方案3】:

    Echo 正在尝试回显$template['foo'] + 1 的结果。由于$template['foo']TemplateData 类型的对象,而1int,因此您收到错误消息。在这种情况下,((string) $template['foo']) 不是“重铸”,它是第一次。

    【讨论】:

    • 感谢 thetaiko; 在这个范围内转换,在 __toString() 已经转换 return (string) $this-&gt;_data; 的意义上重铸此外,在 echo 上转换为 (int) 仍然会引发错误因为那时我试图将一个对象转换为整数。我必须使用字符串并让 PHP 在之后看到算术时强制类型。关于如何完全避免这些问题的任何想法?不幸的是 PHP 还没有(或永远不会)实现 __toInt()__cast()...
    • 啊,我明白你为什么使用(string) 转换为字符串然后添加。但是,对于回显,您尚未在尝试进行加法时调用 __toString(),因为 echo 将尝试仅输出整个表达式的结果。
    • 您可以将__toInt() 方法添加到TemplateData,然后使用:echo $template['foo']-&gt;__toInt() + 1;
    • 或者我想添加一个asInt() 方法以符合您之前的命名约定。
    • 不幸的是,__toString() 在这种情况下对你不起作用。您可以在 Template 类中添加另一种方法。您可以覆盖 getOffset 方法以强制转换为整数,但我怀疑您不希望在每种情况下都这样。
    猜你喜欢
    • 1970-01-01
    • 2015-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-29
    • 2021-06-26
    • 2014-05-02
    • 2019-07-16
    相关资源
    最近更新 更多