将自定义类型对象作为 PHP 服务方法调用的参数传递

发布于 2024-12-10 21:27:33 字数 170 浏览 0 评论 0原文

我们使用 zendamf 作为 Flex 客户端和 PHP 服务器之间的远程网关。 将服务器端类型映射到客户端类型似乎对作为服务方法参数传递的对象没有任何影响。 所有具有自定义类型的对象都作为 stdClass 实例接收。 有没有办法强制这个?或者我们在这里遗漏了什么?

有什么想法吗?

谢谢!

We're using zendamf as a remoting gateway between a flex client and a PHP server.
Mapping server side types to client side types doesn't seem to have any affect on objects passed as service method parameters.
All objects that have custom types are received as stdClass instances.
Is there a way to force this? Or are we missing someting here?

Any thoughts?

Thx!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

恋竹姑娘 2024-12-17 21:27:33

这个问题有点老了,但还没有答案。那么让我尝试一下。 ;)

首先@www.Flextras.com:
你是对的。在 PHP 中,还需要进行一些类映射工作。

因此,对于所有对如何使用 Zend Framework 定义类映射感兴趣的人,请看一下下面简短但(在我看来)详细的介绍。如果您知道所有这些,那么请跳过介绍并继续“关于 Zend Framework 中的客户端到服务器映射和 stdClass”

使用 Zend Framework 在 PHP 中进行类映射的方式

正如 Zend Framework 的 Zend_Amf_Server 的文档所描述的,您有 3 个选项提供类映射。您可以在此处找到完整文档(位于页面中间的某个位置) “类型化对象”)

第一个选项

// Use the setClassMap() method of Zend_Amf_Server
require_once '<PathToZendFramework>/Zend/Amf/Server.php';
$server = new Zend_Amf_Server();

$server->setClassMap('MyActionScriptClassName', 'MyPHPClassName');

// the typical package notation for ActionScript class names is also allowed here
$server->setClassMap('com.company.MyActionScriptClassName', 'MyPHPClassName');

这是最灵活的选项,因为您可以显式指定客户端到服务器和服务器到客户端两个方向的类映射。与灵活性相反,你可能会在这里犯更多错误。您将在下面找到使用类映射时要考虑的事项的清单。

第二个选项

// Define a special named public property on each class used for
// serialization/deserialization via AMF
public class MyPHPClassName
{
    public $_explicitType = 'MyActionScriptClassName';

    /* your class definition here */
}

这样做你的类映射会变得更加动态,因为 Zend_Amf_Server 将自动搜索 $_explicitType 属性。因此,您不必像第一个选项那样显式定义类映射。不幸的是,使用 $_explicitType 您无法为映射定义 PHP 类名(这是“更动态”)。再往下您将找到有关此处提出的问题的详细描述。

第三个选项

// Define a special named public method on each class used for
// serialization/deserialization via AMF
public class MyPHPClassName
{
    public function getASClassName()
    {
        return 'MyActionScriptClassName';
    }

    /* your class definition here */
}

通过使用最后一个选项,您将获得最动态的类映射。您可以像我的示例一样简单地返回字符串文字,但这与使用 $_explicitType 选项相同。该方法的优点是让类动态生成 ActionScript 类名。因此,您可以在所有 AMF 类继承的层次结构中最顶层的类上定义 getASClassName()
这里的缺点与第二个选项相同。您无法定义该类映射到的 PHP 类名。

Flex 和 Zend Framework 的类映射清单

如果您在指定类映射时出现错误,Flex 将无法转换为强类型对象。相反,您将获得通用 Object 类的实例。如果发生这种情况,您应该检查是否存在以下一种或多种情况。

在 Flex 方面,

  • 您的类必须有一个 [RemoteClass] 元数据标记。
  • 如果您使用别名 ([RemoteClass(alias="")]) 指定远程类,请检查您是否输入错误。
  • 您的类(包括它继承的所有类)是否定义了在服务器端等效定义的所有公共属性? (可以是 public var ... 或 getter/setter 对)
  • 您是否将公共属性定义为可读写?
  • 您是否使用 [Transient] 元数据标记标记了不用于传输的属性? (这将排除如此标记的属性被序列化/反序列化)

在 Zend Framework 方面,

  • 您是否使用了类映射的三个选项之一?
  • 如果您使用第一个选项,那么您是否输错了 ActionScript 或 PHP 类名? (ActionScript 类名必须与 Flex 应用程序中的 [RemoteClass] 标记中定义的完全相同)

关于 Zend Framework 中的客户端到服务器映射和 stdClass

我还遇到了以下问题:强类型参数传递给服务器端的服务类方法,生成 stdClass。我经常试图找出为什么会发生这种情况,但从未花时间真正找到原因。昨天,在我的一个项目中再次尝试使类映射尽可能动态/通用之后,经过几个小时的研究,我发现 Zend_Amf_Server 在收到 AMF 请求时会做什么。 (顺便说一句:如果我没有在我的 PHP 代码中实现严格的类命名约定,我永远不会找到原因)。

当 AMF 请求传入并且 Zend_Amf_Server 开始解析它时,它将从请求中读取某个远程类别名并尝试自动加载该类。这就是我们想要 Zend_Amf_Server 做的事情,对吧!?但 AMF 服务器仅支持自动处理类名(如 MyClassName)或符合框架的命名约定(如 My_Class_Name)。您无法使用远程类别名(如 com.company.SomeAmfClass )自动映射类。 Zend 期望您指定一个包(代表普通目录路径),并在尝试加载该类之前将所有句点转换为下划线。执行此操作的类是 Zend_Amf_Parse_TypeLoader。 AMF 服务器将调用其静态方法 loadType()

下面是该方法的实现(直接从 Zend Framework 1.11.7 复制):

/**
  * Load the mapped class type into a callback.
  *
  * @param  string $className
  * @return object|false
  */
 public static function loadType($className)
 {
     $class    = self::getMappedClassName($className);
     if(!$class) {
         $class = str_replace('.', '_', $className);
     }
     if (!class_exists($class)) {
         return "stdClass";
     }
     return $class;
 }

首先,我尝试定义一个实现 Zend_Loader_Autoloader_Interface 的类,并将其推送到 Zend_Loader_Autoloader singleton 的自动加载器堆栈。但这里的问题是 Zend_Amf_Parse_TypeLoader::loadType() 在自动加载发生之前修改了类名 - 调用 class_exists() 时会发生什么。

因此,您可以做的就是直接在框架中更改该方法,以另一种方式处理 com.company.SomeAmfClass 等 ActionScript 类名。但这应该是最后一个选择,因为更改框架代码是不好的做法!

另一个解决方案 - 几乎是最干净的 - 是重新组织和重命名所有类以适应 Zend Framework 的命名约定,但不幸的是,这可能需要数小时的重构(这在 PHP 中并不容易处理),并且可能需要数天的测试和测试调试整个代码库。

为了取得平衡,您可以对所需的框架类进行猴子修补。因此,您不会更改框架本身,但也不会重构您的代码库。有一些框架可以解决这个问题。我在 这个问题的答案中找到了一些

示例这个问题在这里:
AMF 类型对象从 Flex 返回到 PHP
您应该检查它是否符合您的需求。

希望这对某人有帮助。如果我忘记了什么(例如在清单中!),请告诉我

A bit older this question but not answered, yet. So let me try. ;)

First @www.Flextras.com:
You are right. In PHP it is also necessary to do some class mapping stuff.

So for all of you interested in how to define class mapping by using the Zend Framework, take a look at the following short but (in my opinion) detailed introduction. If you know all that then skip introduction and go ahead with "About client-to-server mapping and stdClass in Zend Framework"

The ways of class mapping in PHP using the Zend Framework

As the documentation of the Zend Framework's Zend_Amf_Server describes you have 3 options to provide class mapping. You can find the full documentation here (somewhere in the middle of the page under "Typed Objects").

First option

// Use the setClassMap() method of Zend_Amf_Server
require_once '<PathToZendFramework>/Zend/Amf/Server.php';
$server = new Zend_Amf_Server();

$server->setClassMap('MyActionScriptClassName', 'MyPHPClassName');

// the typical package notation for ActionScript class names is also allowed here
$server->setClassMap('com.company.MyActionScriptClassName', 'MyPHPClassName');

This is the most flexible option because you can dictate the class mappings explicitly for both directions client-to-server and server-to-client. In contrast to flexibility you can make more mistakes here. You will find a checklist below what to consider when using class mapping.

Second option

// Define a special named public property on each class used for
// serialization/deserialization via AMF
public class MyPHPClassName
{
    public $_explicitType = 'MyActionScriptClassName';

    /* your class definition here */
}

Doing it this way your class mapping becomes a bit more dynamic, because Zend_Amf_Server will search automatically for the $_explicitType property. So you do not have to define a class mapping explicitly like in the first option. Unfortunately using the $_explicitType you cannot define the PHP class name for the mapping (this is the "bit more dynamic"). Further down you will find a detailed description about the problem which raises here.

Third option

// Define a special named public method on each class used for
// serialization/deserialization via AMF
public class MyPHPClassName
{
    public function getASClassName()
    {
        return 'MyActionScriptClassName';
    }

    /* your class definition here */
}

By using this last option you will get the most dynamics out of class mapping. You can simply return a string literal like in my example, but this would be the same as using the $_explicitType option. The advantage of the method approach is to let classes generate the ActionScript class name dynamically. So you could define getASClassName() on the top-most class in hierarchy that all your AMF classes inherit from.
The drawback here is the same as for the second option. You cannot define the PHP class name the class is mapped to.

Class Mapping Checklist for Flex and Zend Framework

If you made something wrong in specifying class mapping, Flex will not be able to convert to strongly typed objects. Instead you will get an instance of the generic Object class. If this happens you should check whether one ore more of the following things are the case.

On Flex side

  • Your class has to have a [RemoteClass] metadata tag.
  • If you specified the remote class with an alias ([RemoteClass(alias="")]), then check whether you have made a typing error.
  • Does your class (including all classes it inherits from) define all the public properties defined on the server side equivalent? (can be either a public var ... or a getter/setter pair)
  • Did you define the public properties as readwrite?
  • Did you mark properties not to use for transfer with the [Transient] metadata tag? (This will exclude the so-marked property from being serialzed/deserialized)

On Zend Framework side

  • Did you used one of the three options for class mapping?
  • If you used the first option, then did you mistype the either the ActionScript or the PHP class name? (The ActionScript class name has to be the exact same as defined in the [RemoteClass] tag in your Flex application)

About client-to-server mapping and stdClass in Zend Framework

I also ran into the problem that strongly typed arguments passed to service class methods on server side resulting in stdClass. I often tried to find out why this happens but never took the time to really find the reason. Yesterday, after an additional attempt to make class mapping as dynamic/generic as possible in one of my projects and after several hours of research I found what Zend_Amf_Server will do when getting an AMF request. (BTW: I had never found the reason if I hadn't implement strict class naming conventions into my PHP code).

When the AMF request is coming in and Zend_Amf_Server starts parsing it, it will read the some remote class alias from the request and tries to load that class automatically. That's what we want Zend_Amf_Server to do, right!? But the AMF server supports only automatisms for handling class names like MyClassName or conform to the framework's naming conventions something like My_Class_Name. You cannot map automatically classes with a remote class alias like com.company.SomeAmfClass. Zend expects that you specified a package (what represents an ordinary directory path) and converts all the periods to underscores before making an attempt to load the class. The class that will do this stuff is Zend_Amf_Parse_TypeLoader. The AMF server will call the static method loadType() on it.

Here's the implementation of that method (copied directly out of Zend Framework 1.11.7):

/**
  * Load the mapped class type into a callback.
  *
  * @param  string $className
  * @return object|false
  */
 public static function loadType($className)
 {
     $class    = self::getMappedClassName($className);
     if(!$class) {
         $class = str_replace('.', '_', $className);
     }
     if (!class_exists($class)) {
         return "stdClass";
     }
     return $class;
 }

First I've tried to define a class that implements Zend_Loader_Autoloader_Interface and pushed it onto the Zend_Loader_Autoloader singleton's autoloader stack. But the problem here was that Zend_Amf_Parse_TypeLoader::loadType() modifies the class name before autoloading takes place - what happens when class_exists() is invoked.

So what you could do is to change that method directly in the framework to handle ActionScript class names like com.company.SomeAmfClass in an additional way. But this should be the very last option, because its bad practice to change framework code!

Another solution - and almost the cleanest - would be to reorganize and rename all your classes to fit into the Zend Framework's naming conventions, but unfortunately this may take hours of refactoring (what's not really easy to handle in PHP) and maybe days of testing and debugging your whole codebase.

To strike a balance you could monkey-patch the required framework classes. So you would not change the framework itself but do not refactor your codebase. There are some frameworks that will do the trick. I found some examples in the answers of this question

Additionally found this question here:
AMF typed objects from flex back to PHP
You should check whether it fits your needs.

Hope this will be helpful to someone. Tell me if I forgot something (for example in the checklist!)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文