/stats/core/Settings/Setting.php
PHP | 342 lines | 180 code | 49 blank | 113 comment | 27 complexity | c3511a307a535755945858fefec0e9d8 MD5 | raw file
Possible License(s): BSD-3-Clause, MPL-2.0-no-copyleft-exception, GPL-3.0, GPL-2.0, WTFPL, BSD-2-Clause, LGPL-2.1, Apache-2.0, MIT, AGPL-3.0
- <?php
- /**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
- namespace Piwik\Settings;
- use Piwik\Piwik;
- use Piwik\Settings\Storage\Storage;
- use Exception;
- use Piwik\Validators\BaseValidator;
- /**
- * Base setting type class.
- *
- * @api
- */
- class Setting
- {
- /**
- * The name of the setting
- * @var string
- */
- protected $name;
- /**
- * Null while not initialized, bool otherwise.
- * @var null|bool
- */
- protected $hasWritePermission = null;
- /**
- * @var Storage
- */
- protected $storage;
- /**
- * @var string
- */
- protected $pluginName;
- /**
- * @var FieldConfig
- */
- protected $config;
- /**
- * @var \Closure|null
- */
- protected $configureCallback;
- /**
- * @var mixed
- */
- protected $defaultValue;
- /**
- * @var string
- */
- protected $type;
- /**
- * Constructor.
- *
- * @param string $name The setting's persisted name. Only alphanumeric characters are allowed, eg,
- * `'refreshInterval'`.
- * @param mixed $defaultValue Default value for this setting if no value was specified.
- * @param string $type Eg an array, int, ... see SettingConfig::TYPE_* constants
- * @param string $pluginName The name of the plugin the setting belongs to
- * @throws Exception
- */
- public function __construct($name, $defaultValue, $type, $pluginName)
- {
- if (!ctype_alnum(str_replace('_', '', $name))) {
- $msg = sprintf('The setting name "%s" in plugin "%s" is invalid. Only underscores, alpha and numerical characters are allowed', $name, $pluginName);
- throw new Exception($msg);
- }
- $this->name = $name;
- $this->type = $type;
- $this->pluginName = $pluginName;
- $this->setDefaultValue($defaultValue);
- }
- /**
- * Get the name of the setting.
- * @return string
- */
- public function getName()
- {
- return $this->name;
- }
- /**
- * Get the PHP type of the setting.
- * @return string
- */
- public function getType()
- {
- return $this->type;
- }
- /**
- * @internal
- * @ignore
- * @param $callback
- */
- public function setConfigureCallback($callback)
- {
- $this->configureCallback = $callback;
- $this->config = null;
- }
- /**
- * @return mixed
- */
- public function getDefaultValue()
- {
- return $this->defaultValue;
- }
- /**
- * Sets/overwrites the current default value
- * @param string $defaultValue
- */
- public function setDefaultValue($defaultValue)
- {
- $this->defaultValue = $defaultValue;
- }
- /**
- * @internal
- * @param Storage $storage
- */
- public function setStorage(Storage $storage)
- {
- $this->storage = $storage;
- }
- /**
- * @internal
- * @ignore
- * @return FieldConfig
- * @throws Exception
- */
- public function configureField()
- {
- if (!$this->config) {
- $this->config = new FieldConfig();
- if ($this->configureCallback) {
- call_user_func($this->configureCallback, $this->config);
- }
- $this->setUiControlIfNeeded($this->config);
- $this->checkType($this->config);
- }
- return $this->config;
- }
- /**
- * Set whether setting is writable or not. For example to hide setting from the UI set it to false.
- *
- * @param bool $isWritable
- */
- public function setIsWritableByCurrentUser($isWritable)
- {
- $this->hasWritePermission = (bool) $isWritable;
- }
- /**
- * Returns `true` if this setting is writable for the current user, `false` if otherwise. In case it returns
- * writable for the current user it will be visible in the Plugin settings UI.
- *
- * @return bool
- */
- public function isWritableByCurrentUser()
- {
- return (bool) $this->hasWritePermission;
- }
- /**
- * Saves (persists) the value for this setting in the database if a value has been actually set.
- */
- public function save()
- {
- $this->storage->save();
- }
- /**
- * Returns the previously persisted setting value. If no value was set, the default value
- * is returned.
- *
- * @return mixed
- */
- public function getValue()
- {
- return $this->storage->getValue($this->name, $this->defaultValue, $this->type);
- }
- /**
- * Sets and persists this setting's value overwriting any existing value.
- *
- * Before a value is actually set it will be made sure the current user is allowed to change the value. The value
- * will be first validated either via a system built-in validate method or via a set {@link FieldConfig::$validate}
- * custom method. Afterwards the value will be transformed via a possibly specified {@link FieldConfig::$transform}
- * method. Before storing the actual value, the value will be converted to the actually specified {@link $type}.
- *
- * @param mixed $value
- * @throws \Exception If the current user is not allowed to change the value of this setting.
- */
- public function setValue($value)
- {
- $this->checkHasEnoughWritePermission();
- $config = $this->configureField();
- $this->validateValue($value);
- if ($config->transform && $config->transform instanceof \Closure) {
- $value = call_user_func($config->transform, $value, $this);
- }
- if (isset($this->type) && !is_null($value)) {
- settype($value, $this->type);
- }
- $this->storage->setValue($this->name, $value);
- }
- private function validateValue($value)
- {
- $config = $this->configureField();
- if (!empty($config->validators)) {
- BaseValidator::check($config->title, $value, $config->validators);
- }
- if ($config->validate && $config->validate instanceof \Closure) {
- call_user_func($config->validate, $value, $this);
- } elseif (is_array($config->availableValues)) {
- if (is_bool($value) && $value) {
- $value = '1';
- } elseif (is_bool($value)) {
- $value = '0';
- }
- // TODO move error message creation to a subclass, eg in MeasurableSettings we do not want to mention plugin name
- $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed',
- array(strip_tags($config->title), $this->pluginName));
- if (is_array($value) && $this->type === FieldConfig::TYPE_ARRAY) {
- foreach ($value as $val) {
- if (!array_key_exists($val, $config->availableValues)) {
- throw new \Exception($errorMsg);
- }
- }
- } else {
- if (!array_key_exists($value, $config->availableValues)) {
- throw new \Exception($errorMsg);
- }
- }
- } elseif ($this->type === FieldConfig::TYPE_INT || $this->type === FieldConfig::TYPE_FLOAT) {
- if (!is_numeric($value)) {
- $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed',
- array(strip_tags($config->title), $this->pluginName));
- throw new \Exception($errorMsg);
- }
- } elseif ($this->type === FieldConfig::TYPE_BOOL) {
- if (!in_array($value, array(true, false, '0', '1', 0, 1), true)) {
- $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingsValueNotAllowed',
- array(strip_tags($config->title), $this->pluginName));
- throw new \Exception($errorMsg);
- }
- }
- }
- /**
- * @throws \Exception
- */
- private function checkHasEnoughWritePermission()
- {
- if (!$this->isWritableByCurrentUser()) {
- $errorMsg = Piwik::translate('CoreAdminHome_PluginSettingChangeNotAllowed', array($this->name, $this->pluginName));
- throw new \Exception($errorMsg);
- }
- }
- private function setUiControlIfNeeded(FieldConfig $field)
- {
- if (!isset($field->uiControl)) {
- $defaultControlTypes = array(
- FieldConfig::TYPE_INT => FieldConfig::UI_CONTROL_TEXT,
- FieldConfig::TYPE_FLOAT => FieldConfig::UI_CONTROL_TEXT,
- FieldConfig::TYPE_STRING => FieldConfig::UI_CONTROL_TEXT,
- FieldConfig::TYPE_BOOL => FieldConfig::UI_CONTROL_CHECKBOX,
- FieldConfig::TYPE_ARRAY => FieldConfig::UI_CONTROL_MULTI_SELECT,
- );
- if (isset($defaultControlTypes[$this->type])) {
- $field->uiControl = $defaultControlTypes[$this->type];
- } else {
- $field->uiControl = FieldConfig::UI_CONTROL_TEXT;
- }
- }
- }
- private function checkType(FieldConfig $field)
- {
- if ($field->uiControl === FieldConfig::UI_CONTROL_MULTI_SELECT &&
- $this->type !== FieldConfig::TYPE_ARRAY) {
- throw new Exception('Type must be an array when using a multi select');
- }
- if ($field->uiControl === FieldConfig::UI_CONTROL_MULTI_TUPLE &&
- $this->type !== FieldConfig::TYPE_ARRAY) {
- throw new Exception('Type must be an array when using a multi pair');
- }
- $types = array(
- FieldConfig::TYPE_INT,
- FieldConfig::TYPE_FLOAT,
- FieldConfig::TYPE_STRING,
- FieldConfig::TYPE_BOOL,
- FieldConfig::TYPE_ARRAY
- );
- if (!in_array($this->type, $types)) {
- throw new Exception('Type does not exist');
- }
- }
- }