Table of Contents Previous Next
Logo
Ice Extension for PHP : 24.4 Client-Side Slice-to-PHP Mapping
Copyright © 2003-2008 ZeroC, Inc.

24.4 Client-Side Slice-to-PHP Mapping

This section describes the PHP mapping for Slice types.

24.4.1 Mapping for Identifiers

Slice identifiers map to PHP identifiers of the same name, unless the Slice identifier conflicts with a PHP reserved word, in which case the mapped identifier is prefixed with an underscore. For example, the Slice identifier echo is mapped as _echo.
A flattened mapping is used for identifiers defined within Slice modules because PHP does not have an equivalent to C++ namespaces or Java packages. The flattened mapping uses underscores to separate the components of a fully-scoped name. For example, consider the following Slice definition:
module M {
    enum E { one, two, three };
};
In this case, the Slice identifier M::E is flattened to the PHP identifier M_E.
Note that when checking for a conflict with a PHP reserved word, only the fully-scoped, flattened identifier is considered. For example, the Slice identifier M::function is mapped as M_function, despite the fact that function is a PHP reserved word. There is no need to map it as M__function (with two underscores) because M_function does not conflict with a PHP reserved word.
However, it is still possible for the flattened mapping to generate identifiers that conflict with PHP reserved words. For instance, the Slice identifier require::once must be mapped as _require_once in order to avoid conflict with the PHP reserved word require_once.

24.4.2 Mapping for Simple Built-in Types

PHP has a limited set of primitive types: boolean, integer, double, and string. The Slice built-in types are mapped to PHP types as shown in Table 24.2.
PHP’s integer type may not accommodate the range of values supported by Slice’s long type, therefore long values that are outside this range are mapped as strings. Scripts must be prepared to receive an integer or string from any operation that returns a long value.

24.4.3 Mapping for User-Defined Types

Slice supports user-defined types: enumerations, structures, sequences, and dictionaries.

Mapping for Enumerations

A Slice enumeration maps to a PHP class containing a constant definition for each enumerator. For example:
enum Fruit { Apple, Pear, Orange };
The PHP mapping is shown below:
class Fruit {
    const Apple = 0;
    const Pear = 1;
    const Orange = 2;
}
The enumerators can be accessed in PHP code as Fruit::Apple, etc.

Mapping for Structures

A Slice structure maps to a PHP class containing a public variable for each member of the structure. The class also provides a constructor whose arguments correspond to the data members. This allows you to instantiate and initialize the class in a single statement (instead of having to first instantiate the class and then assign to its members). Each argument provides a default value appropriate for the member’s type.
For example, here is our Employee structure from Section 4.9.4 yet again:
struct Employee {
    long number;
    string firstName;
    string lastName;
};
This structure is mapped to the following PHP class:
class Employee {
    function __construct($number=0, $firstName='', $lastName='')
    {
        // ...
    }

    public $number;
    public $firstName;
    public $lastName;
}

Mapping for Sequences

Slice sequences are mapped to native PHP indexed arrays. The first element of the Slice sequence is contained at index 0 (zero) of the PHP array, followed by the remaining elements in ascending index order.
Here is the definition of our FruitPlatter sequence from Section 4.9.3:
sequence<Fruit> FruitPlatter;
You can create an instance of this sequence as shown below:
// Make a small platter with one Apple and one Orange
//
$platter = array(Fruit::Apple, Fruit::Orange);
The Ice run time validates the elements of an array to ensure that they are compatible with the declared type and reports an error if an incompatible type is encountered.

Mapping for Dictionaries

Slice dictionaries map to native PHP associative arrays. The PHP mapping does not currently support all Slice dictionary types, however, because native PHP associative arrays support only integer and string key types. A Slice dictionary whose key type is boolean, byte, short, int or long is mapped as an associative array with an integer key.1 A Slice dictionary with a string key type is mapped as associative array with a string key. All other key types cause a warning to be generated.
Here is the definition of our EmployeeMap from Section 4.9.4:
dictionary<long, Employee> EmployeeMap;
You can create an instance of this dictionary as shown below:
$e1 = new Employee;
$e1>number = 42;
$e1>firstName = "Stan";
$e1>lastName = "Lipmann";

$e2 = new Employee;
$e2>number = 77;
$e2>firstName = "Herb";
$e2>lastName = "Sutter";

$em = array($e1>number => $e1, $e2>number => $e2);

24.4.4 Mapping for Constants

Slice constant definitions map to corresponding calls to the PHP function define. Here are the constant definitions we saw in Section 4.9.5:
const bool      AppendByDefault = true;
const byte      LowerNibble = 0x0f;
const string    Advice = "Don't Panic!";
const short     TheAnswer = 42;
const double    PI = 3.1416;

enum Fruit { Apple, Pear, Orange };
const Fruit     FavoriteFruit = Pear;
Here are the equivalent PHP definitions for these constants:
define("AppendByDefault", true);
define("LowerNibble", 15);
define("Advice", "Don't Panic!");
define("TheAnswer", 42);
define("PI", 3.1416);

class Fruit {
    const Apple = 0;
    const Pear = 1;
    const Orange = 2;
}
define("FavoriteFruit", Fruit::Pear);

24.4.5 Mapping for Exceptions

A Slice exception maps to a PHP class. For each exception member, the corresponding class contains a public variable of the same name. The class also provides a constructor whose arguments correspond to the data members. This allows you to instantiate and initialize the class in a single statement (instead of having to first instantiate the class and then assign to its members). Each argument provides a default value appropriate for the member’s type. For derived exceptions, the constructor accepts one argument for each base exception member, plus one argument for each derived exception member, in base-to-derived order.
All user exceptions ultimately derive from Ice_UserException (which, in turn, derives from Ice_Exception, which derives from PHP’s base Exception class):
abstract class Ice_Exception extends Exception {
    function __construct($_message = '') {
        ...
    }
}

abstract class Ice_UserException extends Ice_Exception {
    function __construct($_message = '') {
        ...
    }
}
The optional string argument to the constructor is passed unmodified to the Exception constructor.
If the exception derives from a base exception, the corresponding PHP class derives from the mapped class for the base exception. Otherwise, if no base exception is specified, the corresponding class derives from Ice_UserException.
Here is a fragment of the Slice definition for our world time server from Section 4.10.5:
exception GenericError {
    string reason;
};
exception BadTimeVal extends GenericError {};
exception BadZoneName extends GenericError {};
These exceptions are mapped as the following PHP classes:
class GenericError extends Ice_UserException {
    function __construct($_message='', $reason='') {
        ...
    }

    public $reason;
}

class BadTimeVal extends GenericError {
    function __construct($_message='', $reason='') {
        ...
    }
}

class BadZoneName extends GenericError {
    function __construct($_message='', $reason='') {
        ...
    }
}
An application can catch these exceptions as shown below:
try {
    ...
} catch(BadZoneName $ex) {
    // Handle BadZoneName
} catch(GenericError $ex) {
    // Handle GenericError
} catch(Ice_Exception $ex) {
    // Handle all other Ice exceptions
    print_r($ex);
}

24.4.6 Mapping for Run-Time Exceptions

The Ice run time throws run-time exceptions for a number of pre-defined error conditions. All run-time exceptions directly or indirectly derive from Ice_LocalException (which, in turn, derives from Ice_Exception, which derives from PHP’s base Exception class):
abstract class Ice_Exception extends Exception {
    function __construct($_message = '') {
        ...
    }
}

abstract class Ice_LocalException extends Ice_Exception {
    function __construct($_message = '') {
        ...
    }
}
An inheritance diagram for user and run-time exceptions appears in Figure 4.4 on page 112. Note however that the PHP mapping only defines classes for the local exceptions listed below:
• Ice_LocalException
• Ice_UnknownException
• Ice_UnknownLocalException
• Ice_UnknownUserException
• Ice_RequestFailedException
• Ice_ObjectNotExistException
• Ice_FacetNotExistException
• Ice_OperationNotExistException
• Ice_ProtocolException
• Ice_MarshalException
• Ice_NoObjectFactoryException
• Ice_UnexpectedObjectException
Instances of all remaining local exceptions are converted to the class Ice_UnknownLocalException. The unknown member of this class contains a string representation of the original exception.

24.4.7 Mapping for Interfaces

A Slice interface maps to a PHP interface. Each operation in the interface maps to a method of the same name, as described in Section 24.4.9. The inheritance structure of the Slice interface is preserved in the PHP mapping, and all interfaces ultimately derive from Ice_Object:
interface Ice_Object {};
For example, consider the following Slice definitions:
interface A {
    void opA();
};
interface B extends A {
    void opB();
};
These interfaces are mapped to PHP interfaces as shown below:
interface A implements Ice_Object
{
    function opA();
}
interface B implements A
{
    function opB();
}

24.4.8 Mapping for Classes

A Slice class maps to an abstract PHP class. Each operation in the class maps to an abstract method of the same name, as described in Section 24.4.9. For each Slice data member, the PHP class contains a public variable of the same name. The class also provides a constructor whose arguments correspond to the data members. This allows you to instantiate and initialize the class in a single statement (instead of having to first instantiate the class and then assign to its members). Each argument provides a default value appropriate for the member’s type. For derived classes, the constructor accepts one argument for each base class member, plus one argument for each derived class member, in base-to-derived order.
The inheritance structure of the Slice class is preserved in the PHP mapping, and all classes ultimately derive from Ice_ObjectImpl (which, in turn, implements Ice_Object):
class Ice_ObjectImpl implements Ice_Object
{
}
Consider the following class definition:
class TimeOfDay {
    short hour;         // 0  23
    short minute;       // 0  59
    short second;       // 0  59
    string format();    // Return time as hh:mm:ss
};
The PHP mapping for this class is shown below:
abstract class TimeOfDay extends Ice_ObjectImpl
{
    function __construct($hour=0, $minute=0, $second=0) {
        ...
    }

    public $hour;
    public $minute;
    public $second;
    abstract function format();
}

Data Members of Classes

By default, data members of classes are mapped exactly as for structures and exceptions: for each data member in the Slice definition, the generated class contains a corresponding public variable.
If you wish to restrict access to a data member, you can modify its visibility using the protected metadata directive. The presence of this directive causes the Slice compiler to generate the data member with protected visibility. As a result, the member can be accessed only by the class itself or by one of its subclasses. For example, the TimeOfDay class shown below has the protected metadata directive applied to each of its data members:
class TimeOfDay {
    ["protected"] short hour;   // 0  23
    ["protected"] short minute; // 0  59
    ["protected"] short second; // 0  59
    string format();    // Return time as hh:mm:ss
};
The Slice compiler produces the following generated code for this definition:
abstract class TimeOfDay extends Ice_ObjectImpl
{
    function __construct($hour=0, $minute=0, $second=0) {
        ...
    }

    protected $hour;
    protected $minute;
    protected $second;
    abstract function format();
}
For a class in which all of the data members are protected, the metadata directive can be applied to the class itself rather than to each member individually. For example, we can rewrite the TimeOfDay class as follows:
["protected"] class TimeOfDay {
    short hour;         // 0  23
    short minute;       // 0  59
    short second;       // 0  59
    string format();    // Return time as hh:mm:ss
};

Class Factories

Class factories are installed by invoking addObjectFactory on the communicator (see Section 24.4.11). A factory must implement the interface Ice_ObjectFactory, defined as follows:
interface Ice_ObjectFactory implements Ice_LocalObject
{
    function create(/* string */ $id);
    function destroy();
}
For example, we can define and install a factory for the TimeOfDay class as shown below:
class TimeOfDayI extends TimeOfDay {
    function format()
    {
        return sprintf("%02d:%02d:%02d", $this>hour,
                       $this>minute, $this>second);
    }
}

class TimeOfDayFactory extends Ice_LocalObjectImpl
                       implements Ice_ObjectFactory {
    function create($id)
    {
        return new TimeOfDayI;
    }

    function destroy() {}
}

$ICE>addObjectFactory(new TimeOfDayFactory, "::M::TimeOfDay");

24.4.9 Mapping for Operations

Each operation defined in a Slice class or interface is mapped to a PHP function of the same name. Furthermore, each parameter of an operation is mapped to a PHP parameter of the same name. Since PHP is a loosely-typed language, no parameter types are specified.2

In Parameters

The PHP mapping guarantees that the value of an in parameter will not be changed by the invocation.
As an example, here is an interface with operations that pass parameters of various types from client to server:
struct NumberAndString {
    int x;
    string str;
};

sequence<string> StringSeq;

dictionary<long, StringSeq> StringTable;

interface ClientToServer {
    void op1(int i, float f, bool b, string s);
    void op2(NumberAndString ns, StringSeq ss, StringTable st);
    void op3(ClientToServer* proxy);
};
Given a proxy to a ClientToServer interface, the client code can pass parameters as shown below:
$p = ...                                 // Get proxy...

$p>op1(42, 3.14, true, "Hello world!"); // Pass simple literals

$i = 42;
$f = 3.14;
$b = true;
$s = "Hello world!";
$p>op1($i, $f, $b, $s);                 // Pass simple variables

$ns = new NumberAndString();
$ns>x = 42;
$ns>str = "The Answer";
$ss = array("Hello world!");
$st = array(0 => $ss);
$p>op2($ns, $ss, $st);                  // Pass complex variables

$p>op3($p);                             // Pass proxy

Out Parameters

The PHP mapping passes out-parameters by reference. Here is the Slice definition from page 647 once more, modified to pass all parameters in the out direction:
struct NumberAndString {
    int x;
    string str;
};

sequence<string> StringSeq;

dictionary<long, StringSeq> StringTable;

interface ServerToClient {
    int op1(out float f, out bool b, out string s);
    void op2(out NumberAndString ns,
             out StringSeq ss,
             out StringTable st);
    void op3(out ServerToClient* proxy);
};
Given a proxy to a ServerToClient interface, the client code can receive the results as shown below:
$p = ... // Get proxy...
$i = $p>op1(&$f, &$b, &$s);
$p>op2(&$ns, &$ss, &$st);
$p>op3(&$proxy);

Parameter Type Mismatches

The Ice run time performs validation on the arguments to a proxy invocation and reports an error for any type mismatches.

Null Parameters

Some Slice types naturally have "empty" or "not there" semantics. Specifically, sequences, dictionaries, and strings all can be null, but the corresponding Slice types do not have the concept of a null value. To make life with these types easier, whenever you pass null as a parameter or data member of type sequence, dictionary, or string, the Ice run time automatically sends an empty sequence, dictionary, or string to the receiver.
This behavior is useful as a convenience feature: especially for deeply-nested data types, members that are sequences, dictionaries, or strings automatically arrive as an empty value at the receiving end. This saves you having to explicitly initialize, for example, every string element in a large sequence before sending the sequence in order to avoid a run-time error. Note that using null parameters in this way does not create null semantics for Slice sequences, dictionaries, or strings. As far as the object model is concerned, these do not exist (only empty sequences, dictionaries, and strings do). For example, it makes no difference to the receiver whether you send a string as null or as an empty string: either way, the receiver sees an empty string.

24.4.10 Mapping for Proxies

The PHP mapping for Slice proxies uses a single interface, Ice_ObjectPrx, to represent all proxy types.

Typed Versus Untyped Proxies

A proxy can be typed or untyped. All proxies, whether they are typed or untyped, support the core proxy methods described in the next section.
An untyped proxy is equivalent to the Slice type Object*. The communicator operation stringToProxy returns an untyped proxy, as do some of the core proxy methods. A script cannot invoke user-defined operations on an untyped proxy, nor can an untyped proxy be passed as an argument where a typed proxy is expected.
A typed proxy is one that has been associated with a Slice class or interface type. There are two ways a script can obtain a typed proxy:
1. By receiving it as the result of a remote invocation that returns a typed proxy.
2. By using the methods ice_checkedCast or ice_uncheckedCast. The method signatures are shown below:
interface Ice_ObjectPrx {
    /* ... standard proxy methods ... */
    function ice_uncheckedCast(/* string */ $type,
                               /* string */ $facet = null,
                               /* array */ $ctx = null);
    function ice_checkedCast(/* string */ $type,
                             /* string */ $facet = null,
                             /* array */ $ctx = null);
}
These methods, which are equivalent to the static methods checkedCast and uncheckedCast in other Ice language mappings, are defined in PHP as member functions of the proxy class Ice_ObjectPrx. The first argument is a Slice type id, such as "::Demo::Hello", denoting the interface that you intend to access via this proxy. The optional second parameter specifies the name of a facet (see Chapter 30), and the third parameter supplies a request context (see Section 28.11). If you need to supply a context but not a facet, you may also supply the context as the second parameter. The Slice definition for the specified type id must already be loaded.
As an example, suppose a script needs to obtain a typed proxy for interface A, shown below:
interface A {
    void opA();
};
Here are the steps our script performs:
$obj = $ICE>stringToProxy("a:tcp p 12345");
$obj>opA(); // WRONG!
$a = $obj>ice_checkedCast("::A");
$a>opA(); // OK
Attempting to invoke opA on $obj would result in a fatal error because $obj is an untyped proxy.

Using Proxy Methods

The base proxy class Ice_ObjectPrx supports a variety of methods for customizing a proxy (see Section 28.10). Since proxies are immutable, each of these "factory methods" returns a copy of the original proxy that contains the desired modification. For example, you can obtain a proxy configured with a ten second timeout as shown below:
// PHP
$proxy = $ICE->stringToProxy(...);
$proxy = $proxy->ice_timeout(10000);
Most proxy factory methods preserve the proxy’s existing type, allowing you to invoke factory methods without the need to subsequently downcast the new proxy:
// PHP
$base = $ICE->stringToProxy(...);
$hello = $base->ice_checkedCast("::Demo::Hello");
$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 a base proxy that you must subsequently down-cast to an appropriate type.

Request Context

All remote operations on a proxy support an optional final parameter of type Ice::Context representing the request context. The standard PHP mapping for the request context is an associative array in which the keys and values are strings. For example, the code below illustrates how to invoke ice_ping with a request context:
$p = $ICE>stringToProxy("a:tcp p 12345");
$ctx = array("theKey" => "theValue");
$p>ice_ping($ctx);
Alternatively, a script can specify a default request context using the proxy method ice_newContext. See Section 28.11 for more information on request contexts.

Identity

Certain core proxy operations use the type Ice_Identity, which is the PHP mapping for the Slice type Ice::Identity (see Section 28.5). This type is mapped using the standard rules for Slice structures, therefore it is defined as follows:
class Ice_Identity {
    var $name;
    var $category;
}
Two communicator functions are provided for converting Ice_Identity values to and from a string representation. See Section 24.4.11 for details.

Routers and Locators

A PHP script can configure a proxy to use a router or locator via the ice_router and ice_locator factory methods, respectively. These methods are described in Section 28.10.2, but there are special considerations when using PHP. Specifically, the Slice definitions for the Ice::Router and Ice::Locator interfaces must be loaded in order to use these methods.

24.4.11 Mapping for Ice::Communicator

Since the Ice extension for PHP provides only client-side facilities, many of the operations provided by Ice::Communicator operations are not relevant, therefore the PHP mapping supports a subset of the communicator operations. The mapping for Ice::Communicator is shown below:
interface Ice_Communicator {
    function getProperty(/* string */ $name,
                         /* string */ $def = "");
    function stringToProxy(/* string */ $str);
    function proxyToString(/* Ice_ObjectPrx */ $prx);
    function propertyToProxy(/* string */ $property);
    function stringToIdentity(/* string */ $str);
    function identityToString(/* Ice_Identity */ $id);
    function addObjectFactory(/* Ice_ObjectFactory */ $factory,
                              /* string */ $id);
    function findObjectFactory(/* string */ $id);
    function flushBatchRequests();
}
The getProperty method returns the value of a configuration property. If the property is not defined, the method returns the default value if one was provided, otherwise the method returns an empty string.
See Section 28.10.2for a description of the remaining operations.

Communicator Lifecycle

PHP scripts are not allowed to create or destroy communicators. Rather, a communicator is created prior to each PHP request, and is destroyed after the request completes.

Accessing the Communicator

The communicator instance created for a request is available to the script via the global variable $ICE. Scripts should not attempt to assign a different value to this variable. As with any global variable, scripts that need to use $ICE from within a function must declare the variable as global:
function printProxy($prx) {
    global $ICE;

    print $ICE>proxyToString($prx);
}

Communicator Configuration

The profile loaded by the script determines the communicator’s configuration. See Section 24.3 for more information.

1
Boolean values are treated as integers, with false equivalent to 0 (zero) and true equivalent to 1 (one).

2
PHP5 introduces the notion of "type hints" that allow you to specify the formal type of object parameters. This would enable the Slice mapping to specify type hints for parameters of type struct, interface, class and proxy. Unfortunately, PHP5 does not currently allow a parameter defined with a type hint to receive a null value, therefore the Slice mapping does not use type hints for parameters.

Table of Contents Previous Next
Logo