library: lot's of cleanup

This commit is contained in:
Thomas Gelf 2018-06-19 13:44:21 +02:00
parent c2d54f9de9
commit 35815e0dad
9 changed files with 336 additions and 185 deletions

View File

@ -2,7 +2,7 @@
namespace Icinga\Module\Director\IcingaConfig; namespace Icinga\Module\Director\IcingaConfig;
use Icinga\Exception\ProgrammingError; use InvalidArgumentException;
class IcingaLegacyConfigHelper class IcingaLegacyConfigHelper
{ {
@ -34,24 +34,24 @@ class IcingaLegacyConfigHelper
} elseif ($value === 'n') { } elseif ($value === 'n') {
return '0'; return '0';
} else { } else {
throw new ProgrammingError('%s is not a valid boolean', $value); throw new InvalidArgumentException('%s is not a valid boolean', $value);
} }
} }
// TODO: Double-check legacy "encoding" // TODO: Double-check legacy "encoding"
public static function renderString($string) public static function renderString($string)
{ {
$special = array( $special = [
'/\\\/', '/\\\/',
'/\$/', '/\$/',
'/\t/', '/\t/',
'/\r/', '/\r/',
'/\n/', '/\n/',
// '/\b/', -> doesn't work // '/\b/', -> doesn't work
'/\f/' '/\f/',
); ];
$replace = array( $replace = [
'\\\\\\', '\\\\\\',
'\\$', '\\$',
'\\t', '\\t',
@ -59,17 +59,20 @@ class IcingaLegacyConfigHelper
'\\n', '\\n',
// '\\b', // '\\b',
'\\f', '\\f',
); ];
$string = preg_replace($special, $replace, $string); $string = preg_replace($special, $replace, $string);
return $string; return $string;
} }
// Requires an array /**
* @param array $array
* @return string
*/
public static function renderArray($array) public static function renderArray($array)
{ {
$data = array(); $data = [];
foreach ($array as $entry) { foreach ($array as $entry) {
if ($entry instanceof IcingaConfigRenderer) { if ($entry instanceof IcingaConfigRenderer) {
// $data[] = $entry; // $data[] = $entry;

View File

@ -2,18 +2,20 @@
namespace Icinga\Module\Director\Objects; namespace Icinga\Module\Director\Objects;
use Icinga\Exception\ProgrammingError;
use Iterator;
use Countable; use Countable;
use Exception;
use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer; use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer;
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
use InvalidArgumentException;
use Iterator;
class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
{ {
protected $storedArguments = array(); /** @var IcingaCommandArgument[] */
protected $storedArguments = [];
/** @var IcingaCommandArgument[] */ /** @var IcingaCommandArgument[] */
protected $arguments = array(); protected $arguments = [];
protected $modified = false; protected $modified = false;
@ -21,7 +23,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
private $position = 0; private $position = 0;
protected $idx = array(); protected $idx = [];
public function __construct(IcingaObject $object) public function __construct(IcingaObject $object)
{ {
@ -90,7 +92,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
); );
} }
$argument->set('command_id', $this->object->id); $argument->set('command_id', $this->object->get('id'));
$key = $argument->argument_name; $key = $argument->argument_name;
if (array_key_exists($key, $this->arguments)) { if (array_key_exists($key, $this->arguments)) {
@ -114,18 +116,18 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
protected function mungeCommandArgument($key, $value) protected function mungeCommandArgument($key, $value)
{ {
$attrs = array( $attrs = [
'argument_name' => (string) $key, 'argument_name' => (string) $key,
); ];
$map = array( $map = [
'skip_key' => 'skip_key', 'skip_key' => 'skip_key',
'repeat_key' => 'repeat_key', 'repeat_key' => 'repeat_key',
'required' => 'required', 'required' => 'required',
// 'order' => 'sort_order', // 'order' => 'sort_order',
'description' => 'description', 'description' => 'description',
'set_if' => 'set_if', 'set_if' => 'set_if',
); ];
$argValue = null; $argValue = null;
if (is_object($value)) { if (is_object($value)) {
@ -187,7 +189,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
{ {
if (empty($arguments)) { if (empty($arguments)) {
if (count($this->arguments)) { if (count($this->arguments)) {
$this->arguments = array(); $this->arguments = [];
$this->modified = true; $this->modified = true;
} }
@ -218,6 +220,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
/** /**
* Magic isset check * Magic isset check
* *
* @param string $argument
* @return boolean * @return boolean
*/ */
public function __isset($argument) public function __isset($argument)
@ -244,13 +247,13 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
public function add(IcingaCommandArgument $argument) public function add(IcingaCommandArgument $argument)
{ {
if (array_key_exists($argument->argument_name, $this->arguments)) { $name = $argument->get('argument_name');
if (array_key_exists($name, $this->arguments)) {
// TODO: Fail unless $argument equals existing one // TODO: Fail unless $argument equals existing one
return $this; return $this;
} }
$this->arguments[$argument->argument_name] = $argument; $this->arguments[$name] = $argument;
$connection = $this->object->getConnection();
$this->modified = true; $this->modified = true;
$this->refreshIndex(); $this->refreshIndex();
@ -269,13 +272,13 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
$table = $this->object->getTableName(); $table = $this->object->getTableName();
$query = $db->select()->from( $query = $db->select()->from(
array('o' => $table), ['o' => $table],
array() []
)->join( )->join(
array('a' => 'icinga_command_argument'), ['a' => 'icinga_command_argument'],
'o.id = a.command_id', 'o.id = a.command_id',
'*' '*'
)->where('o.object_name = ?', $this->object->object_name) )->where('o.object_name = ?', $this->object->getObjectName())
->order('a.sort_order')->order('a.argument_name'); ->order('a.sort_order')->order('a.argument_name');
$this->arguments = IcingaCommandArgument::loadAll($connection, $query, 'argument_name'); $this->arguments = IcingaCommandArgument::loadAll($connection, $query, 'argument_name');
@ -292,18 +295,18 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
$resolveIds = true $resolveIds = true
) { ) {
if ($chosenProperties !== null) { if ($chosenProperties !== null) {
throw new ProgrammingError( throw new InvalidArgumentException(
'IcingaArguments does not support chosenProperties[]' 'IcingaArguments does not support chosenProperties[]'
); );
} }
$args = array(); $args = [];
foreach ($this->arguments as $arg) { foreach ($this->arguments as $arg) {
if ($arg->shouldBeRemoved()) { if ($arg->shouldBeRemoved()) {
continue; continue;
} }
$args[$arg->argument_name] = $arg->toPlainObject( $args[$arg->get('argument_name')] = $arg->toPlainObject(
$resolved, $resolved,
$skipDefaults, $skipDefaults,
null, null,
@ -316,7 +319,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
public function toUnmodifiedPlainObject() public function toUnmodifiedPlainObject()
{ {
$args = array(); $args = [];
foreach ($this->storedArguments as $key => $arg) { foreach ($this->storedArguments as $key => $arg) {
$args[$arg->argument_name] = $arg->toPlainObject(); $args[$arg->argument_name] = $arg->toPlainObject();
} }
@ -326,7 +329,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
protected function cloneStored() protected function cloneStored()
{ {
$this->storedArguments = array(); $this->storedArguments = [];
foreach ($this->arguments as $k => $v) { foreach ($this->arguments as $k => $v) {
$this->storedArguments[$k] = clone($v); $this->storedArguments[$k] = clone($v);
} }
@ -338,18 +341,19 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
return $arguments->loadFromDb(); return $arguments->loadFromDb();
} }
/**
* @return $this
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
*/
public function store() public function store()
{ {
$db = $this->object->getConnection(); $db = $this->object->getConnection();
$deleted = [];
$dummy = IcingaCommandArgument::create();
$deleted = array();
foreach ($this->arguments as $key => $argument) { foreach ($this->arguments as $key => $argument) {
if ($argument->shouldBeRemoved()) { if ($argument->shouldBeRemoved()) {
$deleted[] = $key; $deleted[] = $key;
} else { } else {
$argument->command_id = $this->object->id; $argument->set('command_id', $this->object->get('id'));
$argument->store($db); $argument->store($db);
} }
} }
@ -360,6 +364,7 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
} }
$this->cloneStored(); $this->cloneStored();
return $this; return $this;
} }
@ -369,13 +374,13 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
return ''; return '';
} }
$args = array(); $args = [];
foreach ($this->arguments as $arg) { foreach ($this->arguments as $arg) {
if ($arg->shouldBeRemoved()) { if ($arg->shouldBeRemoved()) {
continue; continue;
} }
$args[$arg->argument_name] = $arg->toConfigString(); $args[$arg->get('argument_name')] = $arg->toConfigString();
} }
return c::renderKeyOperatorValue('arguments', '+=', c::renderDictionary($args)); return c::renderKeyOperatorValue('arguments', '+=', c::renderDictionary($args));
} }

View File

@ -2,8 +2,8 @@
namespace Icinga\Module\Director\Objects; namespace Icinga\Module\Director\Objects;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
use RuntimeException;
class IcingaCommandArgument extends IcingaObject class IcingaCommandArgument extends IcingaObject
{ {
@ -52,7 +52,7 @@ class IcingaCommandArgument extends IcingaObject
public function isSkippingKey() public function isSkippingKey()
{ {
return $this->skip_key === 'y' || $this->argument_name === null; return $this->get('skip_key') === 'y' || $this->get('argument_name') === null;
} }
// Preserve is not supported // Preserve is not supported
@ -159,13 +159,13 @@ class IcingaCommandArgument extends IcingaObject
$resolveIds = true $resolveIds = true
) { ) {
if ($resolved) { if ($resolved) {
throw new ProgrammingError( throw new RuntimeException(
'A single CommandArgument cannot be resolved' 'A single CommandArgument cannot be resolved'
); );
} }
if ($chosenProperties) { if ($chosenProperties) {
throw new ProgrammingError( throw new RuntimeException(
'IcingaCommandArgument does not support chosenProperties[]' 'IcingaCommandArgument does not support chosenProperties[]'
); );
} }
@ -181,64 +181,65 @@ class IcingaCommandArgument extends IcingaObject
public function toConfigString() public function toConfigString()
{ {
$data = array(); $data = array();
if ($this->argument_value) { $value = $this->get('argument_value');
switch ($this->argument_format) { if ($value) {
switch ($this->get('argument_format')) {
case 'string': case 'string':
$data['value'] = c::renderString($this->argument_value); $data['value'] = c::renderString($value);
break; break;
case 'json': case 'json':
if (is_object($this->argument_value)) { if (is_object($value)) {
$data['value'] = c::renderDictionary($this->argument_value); $data['value'] = c::renderDictionary($value);
} elseif (is_array($this->argument_value)) { } elseif (is_array($value)) {
$data['value'] = c::renderArray($this->argument_value); $data['value'] = c::renderArray($value);
} elseif (is_null($this->argument_value)) { } elseif (is_null($value)) {
// TODO: recheck all this. I bet we never reach this: // TODO: recheck all this. I bet we never reach this:
$data['value'] = 'null'; $data['value'] = 'null';
} elseif (is_bool($this->argument_value)) { } elseif (is_bool($value)) {
$data['value'] = c::renderBoolean($this->argument_value); $data['value'] = c::renderBoolean($value);
} else { } else {
$data['value'] = $this->argument_value; $data['value'] = $value;
} }
break; break;
case 'expression': case 'expression':
$data['value'] = c::renderExpression($this->argument_value); $data['value'] = c::renderExpression($value);
break; break;
} }
} }
if ($this->sort_order !== null) { if ($this->get('sort_order') !== null) {
$data['order'] = $this->sort_order; $data['order'] = $this->get('sort_order');
} }
if ($this->set_if) { if (null !== $this->get('set_if')) {
switch ($this->set_if_format) { switch ($this->get('set_if_format')) {
case 'expression': case 'expression':
$data['set_if'] = c::renderExpression($this->set_if); $data['set_if'] = c::renderExpression($this->get('set_if'));
break; break;
case 'string': case 'string':
default: default:
$data['set_if'] = c::renderString($this->set_if); $data['set_if'] = c::renderString($this->get('set_if'));
break; break;
} }
} }
if ($this->required) { if (null !== $this->get('required')) {
$data['required'] = c::renderBoolean($this->required); $data['required'] = c::renderBoolean($this->get('required'));
} }
if ($this->isSkippingKey()) { if ($this->isSkippingKey()) {
$data['skip_key'] = c::renderBoolean('y'); $data['skip_key'] = c::renderBoolean('y');
} }
if ($this->repeat_key) { if (null !== $this->get('repeat_key')) {
$data['repeat_key'] = c::renderBoolean($this->repeat_key); $data['repeat_key'] = c::renderBoolean($this->get('repeat_key'));
} }
if ($this->description) { if (null !== $this->get('description')) {
$data['description'] = c::renderString($this->description); $data['description'] = c::renderString($this->get('description'));
} }
if (array_keys($data) === array('value')) { if (array_keys($data) === ['value']) {
return $data['value']; return $data['value'];
} else { } else {
return c::renderDictionary($data); return c::renderDictionary($data);

View File

@ -2,14 +2,14 @@
namespace Icinga\Module\Director\Objects; namespace Icinga\Module\Director\Objects;
use Icinga\Exception\ConfigurationError;
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
use RuntimeException;
class IcingaNotification extends IcingaObject class IcingaNotification extends IcingaObject
{ {
protected $table = 'icinga_notification'; protected $table = 'icinga_notification';
protected $defaultProperties = array( protected $defaultProperties = [
'id' => null, 'id' => null,
'object_name' => null, 'object_name' => null,
'object_type' => null, 'object_type' => null,
@ -26,7 +26,7 @@ class IcingaNotification extends IcingaObject
'period_id' => null, 'period_id' => null,
'zone_id' => null, 'zone_id' => null,
'assign_filter' => null, 'assign_filter' => null,
); ];
protected $supportsCustomVars = true; protected $supportsCustomVars = true;
@ -36,29 +36,29 @@ class IcingaNotification extends IcingaObject
protected $supportsApplyRules = true; protected $supportsApplyRules = true;
protected $relatedSets = array( protected $relatedSets = [
'states' => 'StateFilterSet', 'states' => 'StateFilterSet',
'types' => 'TypeFilterSet', 'types' => 'TypeFilterSet',
); ];
protected $multiRelations = array( protected $multiRelations = [
'users' => 'IcingaUser', 'users' => 'IcingaUser',
'user_groups' => 'IcingaUserGroup', 'user_groups' => 'IcingaUserGroup',
); ];
protected $relations = array( protected $relations = [
'zone' => 'IcingaZone', 'zone' => 'IcingaZone',
'host' => 'IcingaHost', 'host' => 'IcingaHost',
'service' => 'IcingaService', 'service' => 'IcingaService',
'command' => 'IcingaCommand', 'command' => 'IcingaCommand',
'period' => 'IcingaTimePeriod', 'period' => 'IcingaTimePeriod',
); ];
protected $intervalProperties = array( protected $intervalProperties = [
'notification_interval' => 'interval', 'notification_interval' => 'interval',
'times_begin' => 'times_begin', 'times_begin' => 'times_begin',
'times_end' => 'times_end', 'times_end' => 'times_end',
); ];
protected function prefersGlobalZone() protected function prefersGlobalZone()
{ {
@ -78,18 +78,15 @@ class IcingaNotification extends IcingaObject
protected function renderTimes_begin() protected function renderTimes_begin()
{ {
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd
$times = (object) array( $times = (object) [
'begin' => c::renderInterval($this->times_begin) 'begin' => c::renderInterval($this->times_begin)
); ];
if ($this->times_end !== null) { if ($this->get('times_end') !== null) {
$times->end = c::renderInterval($this->times_end); $times->end = c::renderInterval($this->get('times_end'));
} }
return c::renderKeyValue( return c::renderKeyValue('times', c::renderDictionary($times));
'times',
c::renderDictionary($times)
);
} }
/** /**
@ -106,18 +103,15 @@ class IcingaNotification extends IcingaObject
{ {
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd
if ($this->times_begin !== null) { if ($this->get('times_begin') !== null) {
return ''; return '';
} }
$times = (object) array( $times = (object) [
'end' => c::renderInterval($this->times_end) 'end' => c::renderInterval($this->get('times_end'))
); ];
return c::renderKeyValue( return c::renderKeyValue('times', c::renderDictionary($times));
'times',
c::renderDictionary($times)
);
} }
/** /**
@ -138,10 +132,10 @@ class IcingaNotification extends IcingaObject
{ {
if ($this->isApplyRule()) { if ($this->isApplyRule()) {
if (($to = $this->get('apply_to')) === null) { if (($to = $this->get('apply_to')) === null) {
throw new ConfigurationError( throw new RuntimeException(sprintf(
'Applied notification "%s" has no valid object type', 'Applied notification "%s" has no valid object type',
$this->getObjectName() $this->getObjectName()
); ));
} }
return sprintf( return sprintf(
@ -161,7 +155,7 @@ class IcingaNotification extends IcingaObject
if (is_int($key)) { if (is_int($key)) {
$this->id = $key; $this->id = $key;
} elseif (is_array($key)) { } elseif (is_array($key)) {
foreach (array('id', 'host_id', 'service_id', 'object_name') as $k) { foreach (['id', 'host_id', 'service_id', 'object_name'] as $k) {
if (array_key_exists($k, $key)) { if (array_key_exists($k, $key)) {
$this->set($k, $key[$k]); $this->set($k, $key[$k]);
} }

View File

@ -6,7 +6,7 @@ use Exception;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterChain; use Icinga\Data\Filter\FilterChain;
use Icinga\Data\Filter\FilterExpression; use Icinga\Data\Filter\FilterExpression;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\CustomVariable\CustomVariables; use Icinga\Module\Director\CustomVariable\CustomVariables;
use Icinga\Module\Director\IcingaConfig\AssignRenderer; use Icinga\Module\Director\IcingaConfig\AssignRenderer;
use Icinga\Module\Director\Data\Db\DbObject; use Icinga\Module\Director\Data\Db\DbObject;
@ -21,6 +21,7 @@ use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
use Icinga\Module\Director\Repository\IcingaTemplateRepository; use Icinga\Module\Director\Repository\IcingaTemplateRepository;
use InvalidArgumentException; use InvalidArgumentException;
use LogicException; use LogicException;
use RuntimeException;
abstract class IcingaObject extends DbObject implements IcingaConfigRenderer abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
{ {
@ -62,22 +63,22 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
protected $type; protected $type;
/* key/value!! */ /* key/value!! */
protected $booleans = array(); protected $booleans = [];
// Property suffixed with _id must exist // Property suffixed with _id must exist
protected $relations = array( protected $relations = [
// property => PropertyClass // property => PropertyClass
); ];
protected $relatedSets = array( protected $relatedSets = [
// property => ExtensibleSetClass // property => ExtensibleSetClass
); ];
protected $multiRelations = array( protected $multiRelations = [
// property => IcingaObjectClass // property => IcingaObjectClass
); ];
protected $loadedMultiRelations = array(); protected $loadedMultiRelations = [];
/** /**
* Allows to set properties pointing to related objects by name without * Allows to set properties pointing to related objects by name without
@ -85,18 +86,18 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
* *
* @var array * @var array
*/ */
protected $unresolvedRelatedProperties = array(); protected $unresolvedRelatedProperties = [];
protected $loadedRelatedSets = array(); protected $loadedRelatedSets = [];
// Will be rendered first, before imports // Will be rendered first, before imports
protected $prioritizedProperties = array(); protected $prioritizedProperties = [];
protected $propertiesNotForRendering = array( protected $propertiesNotForRendering = [
'id', 'id',
'object_name', 'object_name',
'object_type', 'object_type',
); ];
/** /**
* Array of interval property names * Array of interval property names
@ -106,7 +107,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
* *
* @var array * @var array
*/ */
protected $intervalProperties = array(); protected $intervalProperties = [];
/** @var Db */ /** @var Db */
protected $connection; protected $connection;
@ -123,7 +124,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
private $shouldBeRemoved = false; private $shouldBeRemoved = false;
private $resolveCache = array(); private $resolveCache = [];
private $cachedPlainUnmodified; private $cachedPlainUnmodified;
@ -294,23 +295,47 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $this->relations[$property]; return $this->relations[$property];
} }
/**
* @param $property
* @return IcingaObject
*/
public function getRelated($property) public function getRelated($property)
{ {
return $this->getRelatedObject($property, $this->{$property . '_id'}); return $this->getRelatedObject($property, $this->{$property . '_id'});
} }
/**
* @param $property
* @param $id
* @return string
*/
protected function getRelatedObjectName($property, $id) protected function getRelatedObjectName($property, $id)
{ {
return $this->getRelatedObject($property, $id)->object_name; return $this->getRelatedObject($property, $id)->getObjectName();
} }
/**
* @param $property
* @param $id
* @return IcingaObject
*/
protected function getRelatedObject($property, $id) protected function getRelatedObject($property, $id)
{ {
/** @var IcingaObject $class */ /** @var IcingaObject $class */
$class = $this->getRelationClass($property); $class = $this->getRelationClass($property);
return $class::loadWithAutoIncId($id, $this->connection); try {
$object = $class::loadWithAutoIncId($id, $this->connection);
} catch (NotFoundError $e) {
throw new RuntimeException($e->getMessage(), 0, $e);
} }
return $object;
}
/**
* @param $property
* @return IcingaObject|null
*/
public function getResolvedRelated($property) public function getResolvedRelated($property)
{ {
$id = $this->getSingleResolvedProperty($property . '_id'); $id = $this->getSingleResolvedProperty($property . '_id');
@ -336,7 +361,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
/** @var static $class */ /** @var static $class */
$class = self::classByType($type); $class = self::classByType($type);
/** @var static $dummy */ /** @var static $dummy */
$dummy = $class::create(array(), $db); $dummy = $class::create([], $db);
$dummy->prefetchAllRelatedTypes(); $dummy->prefetchAllRelatedTypes();
} }
@ -496,20 +521,30 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $this; return $this;
} }
/**
* @param $name
*/
protected function resolveUnresolvedRelatedProperty($name) protected function resolveUnresolvedRelatedProperty($name)
{ {
$short = substr($name, 0, -3); $short = substr($name, 0, -3);
/** @var IcingaObject $class */ /** @var IcingaObject $class */
$class = $this->getRelationClass($short); $class = $this->getRelationClass($short);
try {
$object = $class::load( $object = $class::load(
$this->unresolvedRelatedProperties[$name], $this->unresolvedRelatedProperties[$name],
$this->connection $this->connection
); );
} catch (NotFoundError $e) {
throw new RuntimeException($e->getMessage(), 0, $e);
}
$this->reallySet($name, $object->get('id')); $this->reallySet($name, $object->get('id'));
unset($this->unresolvedRelatedProperties[$name]); unset($this->unresolvedRelatedProperties[$name]);
} }
/**
* @return bool
*/
public function hasBeenModified() public function hasBeenModified()
{ {
if (parent::hasBeenModified()) { if (parent::hasBeenModified()) {
@ -573,6 +608,10 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return array_key_exists($name, $this->unresolvedRelatedProperties); return array_key_exists($name, $this->unresolvedRelatedProperties);
} }
/**
* @param $key
* @return mixed
*/
protected function getRelationId($key) protected function getRelationId($key)
{ {
if ($this->hasUnresolvedRelatedProperty($key)) { if ($this->hasUnresolvedRelatedProperty($key)) {
@ -582,6 +621,10 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return parent::get($key); return parent::get($key);
} }
/**
* @param $key
* @return string|null
*/
protected function getRelatedProperty($key) protected function getRelatedProperty($key)
{ {
$idKey = $key . '_id'; $idKey = $key . '_id';
@ -592,13 +635,22 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
if ($id = $this->get($idKey)) { if ($id = $this->get($idKey)) {
/** @var IcingaObject $class */ /** @var IcingaObject $class */
$class = $this->getRelationClass($key); $class = $this->getRelationClass($key);
try {
$object = $class::loadWithAutoIncId($id, $this->connection); $object = $class::loadWithAutoIncId($id, $this->connection);
return $object->get('object_name'); } catch (NotFoundError $e) {
throw new RuntimeException($e->getMessage(), 0, $e);
}
return $object->getObjectName();
} }
return null; return null;
} }
/**
* @param string $key
* @return \Icinga\Module\Director\CustomVariable\CustomVariable|mixed|null
*/
public function get($key) public function get($key)
{ {
if (substr($key, 0, 5) === 'vars.') { if (substr($key, 0, 5) === 'vars.') {
@ -1026,6 +1078,10 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $this->groups()->listGroupNames(); return $this->groups()->listGroupNames();
} }
/**
* @return array
* @throws NotFoundError
*/
public function listInheritedGroupNames() public function listInheritedGroupNames()
{ {
$parents = $this->imports()->getObjects(); $parents = $this->imports()->getObjects();
@ -1046,6 +1102,10 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $this; return $this;
} }
/**
* @return array
* @throws NotFoundError
*/
public function listResolvedGroupNames() public function listResolvedGroupNames()
{ {
$groups = $this->groups()->listGroupNames(); $groups = $this->groups()->listGroupNames();
@ -1056,6 +1116,11 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $groups; return $groups;
} }
/**
* @param $group
* @return bool
* @throws NotFoundError
*/
public function hasGroup($group) public function hasGroup($group)
{ {
if ($group instanceof static) { if ($group instanceof static) {
@ -1139,8 +1204,14 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
} }
} }
/** @var IcingaObject $object */ /** @var IcingaObject[] $imports */
foreach (array_reverse($this->imports()->getObjects()) as $object) { try {
$imports = array_reverse($this->imports()->getObjects());
} catch (NotFoundError $e) {
throw new RuntimeException($e->getMessage(), 0, $e->getMessage());
}
foreach ($imports as $object) {
$v = $object->getSingleResolvedProperty($key); $v = $object->getSingleResolvedProperty($key);
if (null !== $v) { if (null !== $v) {
return $v; return $v;
@ -1368,6 +1439,11 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
&& $this->get('object_type') === 'apply'; && $this->get('object_type') === 'apply';
} }
/**
* @throws NotFoundError
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
* @throws \Zend_Db_Adapter_Exception
*/
protected function storeRelatedObjects() protected function storeRelatedObjects()
{ {
$this $this
@ -1380,6 +1456,9 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
->storeArguments(); ->storeArguments();
} }
/**
* @throws NotFoundError
*/
protected function beforeStore() protected function beforeStore()
{ {
$this->resolveUnresolvedRelatedProperties(); $this->resolveUnresolvedRelatedProperties();
@ -1388,12 +1467,22 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
} }
} }
/**
* @throws NotFoundError
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
* @throws \Zend_Db_Adapter_Exception
*/
public function onInsert() public function onInsert()
{ {
DirectorActivityLog::logCreation($this, $this->connection); DirectorActivityLog::logCreation($this, $this->connection);
$this->storeRelatedObjects(); $this->storeRelatedObjects();
} }
/**
* @throws NotFoundError
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
* @throws \Zend_Db_Adapter_Exception
*/
public function onUpdate() public function onUpdate()
{ {
DirectorActivityLog::logModification($this, $this->connection); DirectorActivityLog::logModification($this, $this->connection);
@ -1454,7 +1543,8 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
} }
/** /**
* @return self * @return $this
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
*/ */
protected function storeArguments() protected function storeArguments()
{ {
@ -1470,7 +1560,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
} }
/** /**
* @return self * @return $this
*/ */
protected function storeRelatedSets() protected function storeRelatedSets()
{ {
@ -1484,7 +1574,9 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
} }
/** /**
* @return self * @return $this
* @throws NotFoundError
* @throws \Zend_Db_Adapter_Exception
*/ */
protected function storeImports() protected function storeImports()
{ {
@ -1571,7 +1663,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
&& array_key_exists('enable_active_checks', $this->defaultProperties) && array_key_exists('enable_active_checks', $this->defaultProperties)
) { ) {
$passive = clone($this); $passive = clone($this);
$passive->enable_active_checks = false; $passive->set('enable_active_checks', false);
$config->configFile( $config->configFile(
'director/master/' . $filename, 'director/master/' . $filename,
@ -1624,6 +1716,12 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $filename; return $filename;
} }
/**
* @param $zoneId
* @param IcingaConfig|null $config
* @return string
* @throws NotFoundError
*/
protected function getNameForZoneId($zoneId, IcingaConfig $config = null) protected function getNameForZoneId($zoneId, IcingaConfig $config = null)
{ {
// TODO: this is still ugly. // TODO: this is still ugly.
@ -1645,13 +1743,13 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
} }
if ($this->hasProperty('zone_id')) { if ($this->hasProperty('zone_id')) {
try {
if (! $this->supportsImports()) { if (! $this->supportsImports()) {
if ($zoneId = $this->get('zone_id')) { if ($zoneId = $this->get('zone_id')) {
return $this->getNameForZoneId($zoneId, $config); return $this->getNameForZoneId($zoneId, $config);
} }
} }
try {
if ($zoneId = $this->getSingleResolvedProperty('zone_id')) { if ($zoneId = $this->getSingleResolvedProperty('zone_id')) {
return $this->getNameForZoneId($zoneId, $config); return $this->getNameForZoneId($zoneId, $config);
} }
@ -1802,7 +1900,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
{ {
return c1::renderKeyValue( return c1::renderKeyValue(
$legacyKey, $legacyKey,
c1::renderBoolean($this->$property) c1::renderBoolean($this->get($property))
); );
} }
@ -1956,12 +2054,12 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
protected function renderBooleanProperty($key) protected function renderBooleanProperty($key)
{ {
return c::renderKeyValue($key, c::renderBoolean($this->$key)); return c::renderKeyValue($key, c::renderBoolean($this->get($key)));
} }
protected function renderPropertyAsSeconds($key) protected function renderPropertyAsSeconds($key)
{ {
return c::renderKeyValue($key, c::renderInterval($this->$key)); return c::renderKeyValue($key, c::renderInterval($this->get($key)));
} }
protected function renderSuffix() protected function renderSuffix()
@ -2406,6 +2504,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
* @param Db $db * @param Db $db
* *
* @return IcingaObject * @return IcingaObject
* @throws NotFoundError
*/ */
public static function loadByType($type, $id, Db $db) public static function loadByType($type, $id, Db $db)
{ {
@ -2499,6 +2598,12 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return static::create((array) $plain, $connection); return static::create((array) $plain, $connection);
} }
/**
* @param IcingaObject $object
* @param null $preserve
* @return $this
* @throws NotFoundError
*/
public function replaceWith(IcingaObject $object, $preserve = null) public function replaceWith(IcingaObject $object, $preserve = null)
{ {
if ($preserve === null) { if ($preserve === null) {
@ -2517,7 +2622,14 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $this; return $this;
} }
// TODO: with rules? What if I want to override vars? Drop in favour of vars.x? /**
* TODO: with rules? What if I want to override vars? Drop in favour of vars.x?
*
* @param IcingaObject $object
* @param bool $replaceVars
* @return $this
* @throws NotFoundError
*/
public function merge(IcingaObject $object, $replaceVars = false) public function merge(IcingaObject $object, $replaceVars = false)
{ {
$object = clone($object); $object = clone($object);
@ -2577,6 +2689,14 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $this; return $this;
} }
/**
* @param bool $resolved
* @param bool $skipDefaults
* @param array|null $chosenProperties
* @param bool $resolveIds
* @return object
* @throws NotFoundError
*/
public function toPlainObject( public function toPlainObject(
$resolved = false, $resolved = false,
$skipDefaults = false, $skipDefaults = false,
@ -2813,6 +2933,13 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return 'director/' . $plural; return 'director/' . $plural;
} }
/**
* @param bool $resolved
* @param bool $skipDefaults
* @param array|null $chosenProperties
* @return string
* @throws NotFoundError
*/
public function toJson( public function toJson(
$resolved = false, $resolved = false,
$skipDefaults = false, $skipDefaults = false,

View File

@ -4,22 +4,22 @@ namespace Icinga\Module\Director\Objects;
use Countable; use Countable;
use Exception; use Exception;
use Icinga\Exception\ProgrammingError;
use Iterator; use Iterator;
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c; use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer; use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer;
use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1; use Icinga\Module\Director\IcingaConfig\IcingaLegacyConfigHelper as c1;
use Icinga\Module\Director\Repository\IcingaTemplateRepository; use Icinga\Module\Director\Repository\IcingaTemplateRepository;
use RuntimeException;
class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
{ {
protected $storedNames = array(); protected $storedNames = [];
/** @var array A list of our imports, key and value are the import name */ /** @var array A list of our imports, key and value are the import name */
protected $imports = array(); protected $imports = [];
/** @var IcingaObject[] A list of all objects we have seen, referred by name */ /** @var IcingaObject[] A list of all objects we have seen, referred by name */
protected $objects = array(); protected $objects = [];
protected $modified = false; protected $modified = false;
@ -28,7 +28,7 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
private $position = 0; private $position = 0;
protected $idx = array(); protected $idx = [];
public function __construct(IcingaObject $object) public function __construct(IcingaObject $object)
{ {
@ -56,6 +56,10 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
return $this->modified; return $this->modified;
} }
/**
* @return IcingaObject|null
* @throws \Icinga\Exception\NotFoundError
*/
public function current() public function current()
{ {
if (! $this->valid()) { if (! $this->valid()) {
@ -82,6 +86,11 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
return array_key_exists($this->position, $this->idx); return array_key_exists($this->position, $this->idx);
} }
/**
* @param $key
* @return IcingaObject|null
* @throws \Icinga\Exception\NotFoundError
*/
public function get($key) public function get($key)
{ {
if (array_key_exists($key, $this->imports)) { if (array_key_exists($key, $this->imports)) {
@ -102,7 +111,7 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
} }
if (! is_array($import)) { if (! is_array($import)) {
$import = array($import); $import = [$import];
} }
$existing = $this->listImportNames(); $existing = $this->listImportNames();
@ -112,13 +121,13 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
return $this; return $this;
} }
$this->imports = array(); $this->imports = [];
return $this->add($import); return $this->add($import);
} }
protected function listNamesForGivenImports($imports) protected function listNamesForGivenImports($imports)
{ {
$list = array(); $list = [];
$class = $this->getImportClass(); $class = $this->getImportClass();
foreach ($imports as $i) { foreach ($imports as $i) {
@ -146,11 +155,11 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
public function clear() public function clear()
{ {
if ($this->imports === array()) { if ($this->imports === []) {
return $this; return $this;
} }
$this->imports = array(); $this->imports = [];
$this->modified = true; $this->modified = true;
return $this->refreshIndex(); return $this->refreshIndex();
@ -171,6 +180,7 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
{ {
$this->idx = array_keys($this->imports); $this->idx = array_keys($this->imports);
// $this->object->templateResolver()->refreshObject($this->object); // $this->object->templateResolver()->refreshObject($this->object);
return $this; return $this;
} }
@ -187,6 +197,7 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
$this->add($i); $this->add($i);
} }
return $this; return $this;
} }
@ -214,10 +225,11 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
/** /**
* @return IcingaObject[] * @return IcingaObject[]
* @throws \Icinga\Exception\NotFoundError
*/ */
public function getObjects() public function getObjects()
{ {
$list = array(); $list = [];
foreach ($this->listImportNames() as $name) { foreach ($this->listImportNames() as $name) {
$list[$name] = $this->getObject($name); $list[$name] = $this->getObject($name);
} }
@ -225,6 +237,11 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
return $list; return $list;
} }
/**
* @param $name
* @return IcingaObject
* @throws \Icinga\Exception\NotFoundError
*/
protected function getObject($name) protected function getObject($name)
{ {
if (array_key_exists($name, $this->objects)) { if (array_key_exists($name, $this->objects)) {
@ -236,13 +253,10 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
$class = $this->getImportClass(); $class = $this->getImportClass();
if (is_array($this->object->getKeyName())) { if (is_array($this->object->getKeyName())) {
// Services only // Services only
$import = $class::load( $import = $class::load([
array(
'object_name' => $name, 'object_name' => $name,
'object_type' => 'template' 'object_type' => 'template'
), ], $connection);
$connection
);
} else { } else {
$import = $class::load($name, $connection); $import = $class::load($name, $connection);
} }
@ -277,7 +291,7 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
$this->objects = IcingaTemplateRepository::instanceByObject($this->object) $this->objects = IcingaTemplateRepository::instanceByObject($this->object)
->getTemplatesIndexedByNameFor($this->object); ->getTemplatesIndexedByNameFor($this->object);
if (empty($this->objects)) { if (empty($this->objects)) {
$this->imports = array(); $this->imports = [];
} else { } else {
$keys = array_keys($this->objects); $keys = array_keys($this->objects);
$this->imports = array_combine($keys, $keys); $this->imports = array_combine($keys, $keys);
@ -287,6 +301,11 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
return $this; return $this;
} }
/**
* @return bool
* @throws \Zend_Db_Adapter_Exception
* @throws \Icinga\Exception\NotFoundError
*/
public function store() public function store()
{ {
if (! $this->hasBeenModified()) { if (! $this->hasBeenModified()) {
@ -295,7 +314,7 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
$objectId = $this->object->get('id'); $objectId = $this->object->get('id');
if ($objectId === null) { if ($objectId === null) {
throw new ProgrammingError( throw new RuntimeException(
'Cannot store imports for unstored object with no ID' 'Cannot store imports for unstored object with no ID'
); );
} else { } else {
@ -318,14 +337,11 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
$weight = 1; $weight = 1;
foreach ($this->getObjects() as $import) { foreach ($this->getObjects() as $import) {
$db->insert( $db->insert($table, [
$table,
array(
$objectCol => $objectId, $objectCol => $objectId,
$importCol => $import->get('id'), $importCol => $import->get('id'),
'weight' => $weight++ 'weight' => $weight++
) ]);
);
} }
$this->cloneStored(); $this->cloneStored();

View File

@ -3,9 +3,9 @@
namespace Icinga\Module\Director\Repository; namespace Icinga\Module\Director\Repository;
use Icinga\Authentication\Auth; use Icinga\Authentication\Auth;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\Db; use Icinga\Module\Director\Db;
use Icinga\Module\Director\Objects\IcingaObject; use Icinga\Module\Director\Objects\IcingaObject;
use RuntimeException;
trait RepositoryByObjectHelper trait RepositoryByObjectHelper
{ {
@ -62,7 +62,6 @@ trait RepositoryByObjectHelper
* @param IcingaObject $object * @param IcingaObject $object
* @param Db|null $connection * @param Db|null $connection
* @return static * @return static
* @throws ProgrammingError
*/ */
public static function instanceByObject(IcingaObject $object, Db $connection = null) public static function instanceByObject(IcingaObject $object, Db $connection = null)
{ {
@ -71,11 +70,11 @@ trait RepositoryByObjectHelper
} }
if (! $connection) { if (! $connection) {
throw new ProgrammingError( throw new RuntimeException(sprintf(
'Cannot use repository for %s "%s" as it has no DB connection', 'Cannot use repository for %s "%s" as it has no DB connection',
$object->getShortTableName(), $object->getShortTableName(),
$object->getObjectName() $object->getObjectName()
); ));
} }
return static::instanceByType( return static::instanceByType(

View File

@ -3,10 +3,9 @@
namespace Icinga\Module\Director\Resolver; namespace Icinga\Module\Director\Resolver;
use Icinga\Application\Benchmark; use Icinga\Application\Benchmark;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotImplementedError;
use Icinga\Module\Director\Db; use Icinga\Module\Director\Db;
use Icinga\Module\Director\Objects\IcingaObject; use Icinga\Module\Director\Objects\IcingaObject;
use RuntimeException;
class TemplateTree class TemplateTree
{ {
@ -59,12 +58,12 @@ class TemplateTree
if (array_key_exists($pid, $this->names)) { if (array_key_exists($pid, $this->names)) {
$parents[] = $this->names[$pid]; $parents[] = $this->names[$pid];
} else { } else {
throw new ConfigurationError( throw new RuntimeException(sprintf(
'Got invalid parent id %d for %s "%s"', 'Got invalid parent id %d for %s "%s"',
$pid, $pid,
$this->type, $this->type,
$object->getObjectName() $object->getObjectName()
); ));
} }
} }
} }
@ -125,7 +124,9 @@ class TemplateTree
if ($object->hasBeenLoadedFromDb()) { if ($object->hasBeenLoadedFromDb()) {
return $this->getParentsById($object->getProperty('id')); return $this->getParentsById($object->getProperty('id'));
} else { } else {
throw new NotImplementedError('Not yet'); throw new RuntimeException(
'Loading parents for unstored objects has not been implemented yet'
);
// return $this->getParentsForUnstoredObject($object); // return $this->getParentsForUnstoredObject($object);
} }
} }
@ -217,7 +218,9 @@ class TemplateTree
if ($object->hasBeenLoadedFromDb()) { if ($object->hasBeenLoadedFromDb()) {
return $this->getChildrenById($object->getProperty('id')); return $this->getChildrenById($object->getProperty('id'));
} else { } else {
throw new NotImplementedError('Not yet'); throw new RuntimeException(
'Loading children for unstored objects has not been implemented yet'
);
// return $this->getChildrenForUnstoredObject($object); // return $this->getChildrenForUnstoredObject($object);
} }
} }
@ -238,7 +241,9 @@ class TemplateTree
if ($object->hasBeenLoadedFromDb()) { if ($object->hasBeenLoadedFromDb()) {
return $this->getDescendantsById($object->getProperty('id')); return $this->getDescendantsById($object->getProperty('id'));
} else { } else {
throw new NotImplementedError('Not yet'); throw new RuntimeException(
'Loading descendants for unstored objects has not been implemented yet'
);
// return $this->getDescendantsForUnstoredObject($object); // return $this->getDescendantsForUnstoredObject($object);
} }
} }
@ -262,7 +267,10 @@ class TemplateTree
if ($parentId === null) { if ($parentId === null) {
return $this->returnFullTree(); return $this->returnFullTree();
} else { } else {
return $this->partialTree($parentId); throw new RuntimeException(
'Partial tree fetching has not been implemented yet'
);
// return $this->partialTree($parentId);
} }
} }

View File

@ -26,7 +26,7 @@ trait FormElementContainer
{ {
if (! array_key_exists($name, $this->elements)) { if (! array_key_exists($name, $this->elements)) {
throw new InvalidArgumentException(sprintf( throw new InvalidArgumentException(sprintf(
'Trying to get inexistant element "%s"', 'Trying to get non existent element "%s"',
$name $name
)); ));
} }
@ -60,10 +60,8 @@ trait FormElementContainer
if ($this instanceof BaseHtmlElement) { if ($this instanceof BaseHtmlElement) {
$element = $this->decorate($this->getElement($name)); $element = $this->decorate($this->getElement($name));
}
//...
$this->add($element); $this->add($element);
}
return $this; return $this;
} }
@ -103,7 +101,7 @@ trait FormElementContainer
* *
* @param $name * @param $name
* @param $type * @param $type
* @param $options * @param $attributes
* @return BaseFormElement * @return BaseFormElement
*/ */
public function createElement($name, $type, $attributes = null) public function createElement($name, $type, $attributes = null)