Table of Contents Previous Next
Logo
Client-Side Slice-to-PHP Mapping : 28.11 Mapping for Interfaces
Copyright © 2003-2010 ZeroC, Inc.

28.11 Mapping for Interfaces

The mapping of Slice interfaces revolves around the idea that, to invoke a remote operation, you call a method on a local class instance that represents the remote object. This makes the mapping easy and intuitive to use because, for all intents and purposes (apart from error semantics), making a remote procedure call is no different from making a local procedure call.

28.11.1 Proxy Objects

Slice interfaces are implemented by instances of the Ice_ObjectPrx class. In the client’s address space, an instance of ObjectPrx is the local ambassador for a remote instance of an interface in a server and is known as a proxy instance. All the details about the server-side object, such as its address, what protocol to use, and its object identity are encapsulated in that instance.
The PHP mapping for proxies differs from that of other Ice language mappings in that the ObjectPrx class is used to implement all Slice interfaces. The primary motivation for this design is minimizing the amount of code that is generated for each interface. As a result, a proxy object returned by the communi­cator operations stringToProxy and propertyToProxy is untyped, meaning it is not associated with a user-defined Slice interface. Once you narrow the proxy to a particular interface, you can use that proxy to invoke your Slice operations.
Proxy instances are always created on behalf of the client by the Ice run time, so client code never has any need to instantiate a proxy directly.
A value of null denotes the null proxy. The null proxy is a dedicated value that indicates that a proxy points “nowhere” (denotes no object).
For each operation in the interface, the proxy object supports a method of the same name. Each operation accepts an optional trailing parameter representing the operation context. This parameter is an associative string array for use by the Ice run time to store information about how to deliver a request. You normally do not need to use it. (We examine the context parameter in detail in Chapter 32. The parameter is also used by IceStorm—see Chapter 44.)

28.11.2 The Ice_ObjectPrx Class

In the PHP language mapping, all proxies are instances of Ice_ObjectPrx. This class provides a number of methods:
class Ice_ObjectPrx
{
    function ice_getIdentity();
    function ice_isA($id);
    function ice_id();
    function ice_ping();
    # ...
}
The methods behave as follows:
• ice_getIdentity
This method returns the identity of the object denoted by the proxy. The iden­tity of an Ice object has the following Slice type:
module Ice {
    struct Identity {
        string name;
        string category;
    };
};
To see whether two proxies denote the same object, first obtain the identity for each object and then compare the identities:
$proxy1 = ...
$proxy2 = ...
$id1 = $proxy1>ice_getIdentity();
$id2 = $proxy2>ice_getIdentity();

if($id1 == $id2)
    // proxy1 and proxy2 denote the same object
else
    // proxy1 and proxy2 denote different objects
• ice_isA
This method determines whether the object denoted by the proxy supports a specific interface. The argument to ice_isA is a type ID (see Section 4.13). For example, to see whether a proxy of type ObjectPrx denotes a Printer object, we can write:
$proxy = ...
if($proxy != null && $proxy>ice_isA("::Printer"))
    // proxy denotes a Printer object
else
    // proxy denotes some other type of object
Note that we are testing whether the proxy is null before attempting to invoke the ice_isA method. This avoids getting a run-time error if the proxy is null.
• ice_id
This method returns the type ID of the object denoted by the proxy. Note that the type returned is the type of the actual object, which may be more derived than the narrowed type of the proxy. For example, if we have a proxy that has been narrowed to the type ID ::Base, the return value of ice_id might be "::Base", or it might be something more derived, such as "::Derived".
• ice_ping
This method provides a basic reachability test for the object. If the object can physically be contacted (that is, the object exists and its server is running and reachable), the call completes normally; otherwise, it throws an exception that indicates why the object could not be reached, such as ObjectNotExistException or ConnectTimeoutException.
The ObjectPrx class also defines an operator for comparing two proxies for equality. Note that all aspects of proxies are compared by this operation, such as the communication endpoints for the proxy. This means that, in general, if two proxies compare unequal, that does not imply that they denote different objects. For example, if two proxies denote the same Ice object via different transport endpoints, == returns false even though the proxies denote the same object.
There are other methods in ObjectPrx, not shown here. These methods provide different ways to dispatch a call. (We discuss these methods in Chapter 32.)

28.11.3 Casting Proxies

The PHP mapping for a proxy generates a class with two static methods. For example, the following class is generated for the Slice interface named Simple:
class SimplePrxHelper
{
    public static function
    checkedCast($proxy, $facetOrCtx=null, $ctx=null);

    public static function
    uncheckedCast($proxy, $facet=null);
}
Both the checkedCast and uncheckedCast methods implement a down-cast: if the passed proxy is a proxy for an object of type Simple, or a proxy for an object with a type derived from Simple, the cast returns a proxy narrowed to that type; otherwise, if the passed proxy denotes an object of a different type (or if the passed proxy is null), the cast returns null.
The method names checkedCast and uncheckedCast are reserved for use in proxies. If a Slice interface defines an operation with either of those names, the mapping escapes the name in the generated proxy by prepending an under­score. For example, an interface that defines an operation named checkedCast is mapped to a proxy with a method named _checkedCast.
The arguments are described below:
• $proxy
The proxy to be narrowed.
• $facetOrCtx
This optional argument can be either a string representing a desired facet (see Chapter 33), or an associative string array representing a context (see Section 32.12).
• $ctx
If $facetOrCtx contains a facet name, use this argument to supply an asso­ciative string array representing a context (see Section 32.12).
• $facet
Specifies the name of the desired facet (see Chapter 33).
Given a proxy of any type, you can use a checkedCast to determine whether the corresponding object supports a given type, for example:
$obj = ...      // Get a proxy from somewhere...

$simple = SimplePrx::checkedCast($obj);
if($simple != null)
    // Object supports the Simple interface...
else
    // Object is not of type Simple...
Note that a checkedCast contacts the server. This is necessary because only the server implementation has definite knowledge of the type of an object. As a result, a checkedCast may throw a ConnectTimeoutException or an ObjectNotExistException.
In contrast, an uncheckedCast does not contact the server and uncondi­tionally returns a proxy of the requested type. However, if you do use an uncheckedCast, you must be certain that the proxy really does support the type you are casting to; otherwise, if you get it wrong, you will most likely get a run-time exception when you invoke an operation on the proxy. The most likely error for such a type mismatch is OperationNotExistException. However, other exceptions, such as a marshaling exception are possible as well. And, if the object happens to have an operation with the correct name, but different parameter types, no exception may be reported at all and you simply end up sending the invocation to an object of the wrong type; that object may do rather non-sensical things. To illustrate this, consider the following two interfaces:
interface Process {
    void launch(int stackSize, int dataSize);
};

// ...

interface Rocket {
    void launch(float xCoord, float yCoord);
};
Suppose you expect to receive a proxy for a Process object and use an uncheckedCast to down-cast the proxy:
$obj = ...                                  // Get proxy...
$process = ProcessPrx::uncheckedCast($obj); // No worries...
$process>launch(40, 60);                   // Oops...
If the proxy you received actually denotes a Rocket object, the error will go unde­tected by the Ice run time: because int and float have the same size and because the Ice protocol does not tag data with its type on the wire, the implementation of Rocket::launch will simply misinterpret the passed integers as floating-point numbers.
In fairness, this example is somewhat contrived. For such a mistake to go unnoticed at run time, both objects must have an operation with the same name and, in addition, the run-time arguments passed to the operation must have a total marshaled size that matches the number of bytes that are expected by the unmar­shaling code on the server side. In practice, this is extremely rare and an incorrect uncheckedCast typically results in a run-time exception.

Backward Compatibility

Prior releases of the PHP language mapping provided two proxy methods for narrowing a proxy:
class Ice_ObjectPrx
{
    function ice_checkedCast($type, $facetOrCtx=null, $ctx=null);
    function ice_uncheckedCast($type, $facet=null);
    # ...
}
For example, a proxy can be narrowed as follows:
$proxy = $proxy->ice_checkedCast("::Demo::Hello");
The disadvantage of this API is that embedded type ID strings in an application are a potential source of defects because the strings are not validated until run time. Although these methods are still supported for the sake of backward compat­ibility, we recommend using the static methods that are generated for each interface. Not only are these static methods consistent with the APIs of other Ice language mappings, they also avoid the need to hard-code type ID strings in your application.

28.11.4 Using Proxy Methods

The base proxy class ObjectPrx supports a variety of methods for customizing a proxy (see Section 32.11). Since proxies are immutable, each of these “factory methods” returns a copy of the original proxy that contains the desired modifica­tion. For example, you can obtain a proxy configured with a ten second timeout as shown below:
$proxy = $communicator->stringToProxy(...);
$proxy = $proxy->ice_timeout(10000);
A factory method returns a new proxy object if the requested modification differs from the current proxy, otherwise it returns the current proxy. With few excep­tions, factory methods return a proxy of the same type as the current proxy, there­fore it is generally not necessary to repeat a down-cast after using a factory method. The example below demonstrates these semantics:
$base = $communicator->stringToProxy(...);
$hello = Demo_HelloPrxHelper::checkedCast($base);
$hello = $hello->ice_timeout(10000); // Type is not discarded
$hello->sayHello();
The only exceptions are the factory methods ice_facet and ice_identity. Calls to either of these methods may produce a proxy for an object of an unrelated type, therefore they return an untyped proxy that you must subsequently down-cast to an appropriate type.

28.11.5 Object Identity and Proxy Comparison

Proxy objects support comparison using the comparison operators == and !=. Note that proxy comparison uses all of the information in a proxy for the compar­ison. This means that not only the object identity must match for a comparison to succeed, but other details inside the proxy, such as the protocol and endpoint information, must be the same. In other words, comparison tests for proxy identity, not object identity. A common mistake is to write code along the following lines:
$p1 = ...        // Get a proxy...
$p2 = ...        // Get another proxy...

if($p1 != $p2)
    // p1 and p2 denote different objects       // WRONG!
else
    // p1 and p2 denote the same object         // Correct
Even though p1 and p2 differ, they may denote the same Ice object. This can happen because, for example, both p1 and p2 embed the same object identity, but each uses a different protocol to contact the target object. Similarly, the protocols may be the same, but denote different endpoints (because a single Ice object can be contacted via several different transport endpoints). In other words, if two proxies compare equal, we know that the two proxies denote the same object (because they are identical in all respects); however, if two proxies compare unequal, we know absolutely nothing: the proxies may or may not denote the same object.
To compare the object identities of two proxies, you can use a helper function in the Ice module:
function Ice_proxyIdentityCompare($lhs, $rhs);
function Ice_proxyIdentityAndFacetCompare($lhs, $rhs);
proxyIdentityCompare allows you to correctly compare proxies for iden­tity:
$p1 = ...        // Get a proxy...
$p2 = ...        // Get another proxy...

if(Ice_proxyIdentityCompare($p1, $p2) != 0)
    // p1 and p2 denote different objects       // Correct
else
    // p1 and p2 denote the same object         // Correct
The function returns 0 if the identities are equal, 1 if p1 is less than p2, and 1 if p1 is greater than p2. (The comparison uses name as the major sort key and category as the minor sort key.)
The proxyIdentityAndFacetCompare function behaves similarly, but compares both the identity and the facet name (see Chapter 33).

Table of Contents Previous Next
Logo