<?php
|
namespace PhpAmqpLib\Wire;
|
|
use PhpAmqpLib\Channel\AMQPChannel;
|
|
/**
|
* Abstract base class for AMQP content. Subclasses should override
|
* the propertyDefinitions attribute.
|
*/
|
abstract class GenericContent
|
{
|
/** @var AMQPChannel[] */
|
public $delivery_info = array();
|
|
/** @var array Final property definitions */
|
protected $prop_types;
|
|
/** @var array Properties content */
|
private $properties = array();
|
|
/** @var string Compiled properties */
|
private $serialized_properties;
|
|
/**
|
* @var array
|
*/
|
protected static $propertyDefinitions = array(
|
'dummy' => 'shortstr'
|
);
|
|
/**
|
* @param array $properties Message property content
|
* @param array $propertyTypes Message property definitions
|
*/
|
public function __construct($properties, $propertyTypes = null)
|
{
|
$this->prop_types = self::$propertyDefinitions;
|
|
if (!empty($propertyTypes)) {
|
$this->prop_types = $propertyTypes;
|
}
|
|
if (!empty($properties)) {
|
$this->properties = array_intersect_key($properties, $this->prop_types);
|
}
|
}
|
|
/**
|
* Check whether a property exists in the 'properties' dictionary
|
* or if present - in the 'delivery_info' dictionary.
|
*
|
* @param string $name
|
* @return bool
|
*/
|
public function has($name)
|
{
|
return isset($this->properties[$name]) || isset($this->delivery_info[$name]);
|
}
|
|
/**
|
* Look for additional properties in the 'properties' dictionary,
|
* and if present - the 'delivery_info' dictionary.
|
*
|
* @param string $name
|
* @throws \OutOfBoundsException
|
* @return mixed|AMQPChannel
|
*/
|
public function get($name)
|
{
|
if (isset($this->properties[$name])) {
|
return $this->properties[$name];
|
}
|
|
if (isset($this->delivery_info[$name])) {
|
return $this->delivery_info[$name];
|
}
|
|
throw new \OutOfBoundsException(sprintf(
|
'No "%s" property',
|
$name
|
));
|
}
|
|
/**
|
* Returns the properties content
|
*
|
* @return array
|
*/
|
public function get_properties()
|
{
|
return $this->properties;
|
}
|
|
/**
|
* Sets a property value
|
*
|
* @param string $name The property name (one of the property definition)
|
* @param mixed $value The property value
|
* @throws \OutOfBoundsException
|
*/
|
public function set($name, $value)
|
{
|
if (!array_key_exists($name, $this->prop_types)) {
|
throw new \OutOfBoundsException(sprintf(
|
'No "%s" property',
|
$name
|
));
|
}
|
|
$this->properties[$name] = $value;
|
}
|
|
/**
|
* Given the raw bytes containing the property-flags and
|
* property-list from a content-frame-header, parse and insert
|
* into a dictionary stored in this object as an attribute named
|
* 'properties'.
|
*
|
* @param AMQPReader $reader
|
* NOTE: do not mutate $reader
|
* @return $this
|
*/
|
public function load_properties(AMQPReader $reader)
|
{
|
// Read 16-bit shorts until we get one with a low bit set to zero
|
$flags = array();
|
|
while (true) {
|
$flag_bits = $reader->read_short();
|
$flags[] = $flag_bits;
|
|
if (($flag_bits & 1) === 0) {
|
break;
|
}
|
}
|
|
$shift = 0;
|
$data = array();
|
|
foreach ($this->prop_types as $key => $proptype) {
|
if ($shift === 0) {
|
if (!$flags) {
|
break;
|
}
|
$flag_bits = array_shift($flags);
|
$shift = 15;
|
}
|
|
if ($flag_bits & (1 << $shift)) {
|
$data[$key] = $reader->{'read_' . $proptype}();
|
}
|
|
$shift -= 1;
|
}
|
|
$this->properties = $data;
|
|
return $this;
|
}
|
|
|
/**
|
* Serializes the 'properties' attribute (a dictionary) into the
|
* raw bytes making up a set of property flags and a property
|
* list, suitable for putting into a content frame header.
|
*
|
* @return string
|
* @todo Inject the AMQPWriter to make the method easier to test
|
*/
|
public function serialize_properties()
|
{
|
if (!empty($this->serialized_properties)) {
|
return $this->serialized_properties;
|
}
|
|
$shift = 15;
|
$flag_bits = 0;
|
$flags = array();
|
$raw_bytes = new AMQPWriter();
|
|
foreach ($this->prop_types as $key => $prototype) {
|
$val = isset($this->properties[$key]) ? $this->properties[$key] : null;
|
|
// Very important: PHP type eval is weak, use the === to test the
|
// value content. Zero or false value should not be removed
|
if ($val === null) {
|
$shift -= 1;
|
continue;
|
}
|
|
if ($shift === 0) {
|
$flags[] = $flag_bits;
|
$flag_bits = 0;
|
$shift = 15;
|
}
|
|
$flag_bits |= (1 << $shift);
|
if ($prototype != 'bit') {
|
$raw_bytes->{'write_' . $prototype}($val);
|
}
|
|
$shift -= 1;
|
}
|
|
$flags[] = $flag_bits;
|
$result = new AMQPWriter();
|
foreach ($flags as $flag_bits) {
|
$result->write_short($flag_bits);
|
}
|
|
$result->write($raw_bytes->getvalue());
|
|
$this->serialized_properties = $result->getvalue();
|
|
return $this->serialized_properties;
|
}
|
}
|