Created
April 6, 2012 08:55
-
-
Save k-holy/2318279 to your computer and use it in GitHub Desktop.
値のセット時に任意のフィルタを行うアクセサクラス
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme; | |
$int_filter = function($value) { | |
if (!preg_match('/\A(?:0|[1-9][0-9]*)\z/', (string)$value)) { | |
throw new \InvalidArgumentException('invalid unsigned integer.'); | |
} | |
return (int)$value; | |
}; | |
$length_checker = function($value, $min = null, $max = null) { | |
$length = mb_strlen($value); | |
if (isset($min) && $length < $min) { | |
throw new \InvalidArgumentException( | |
sprintf('character length must not less than %s.', $min)); | |
} | |
if (isset($max) && $length > $max) { | |
throw new \InvalidArgumentException( | |
sprintf('character length must not greater than %s.', $max)); | |
} | |
return $value; | |
}; | |
$sanitizer = function($value) { | |
return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $value); | |
}; | |
$normalize_newline = function($value) { | |
return str_replace("\r", "\n", str_replace("\r\n", "\n", $value)); | |
}; | |
$user = FilterableAccessor::instance(array( | |
'number' => function($value) use ($int_filter) { | |
return $int_filter($value); | |
}, | |
'name' => function($value) use ($length_checker, $sanitizer) { | |
return $length_checker( | |
$sanitizer($value), 1, 10); | |
}, | |
'comment' => function($value) use ($length_checker, $sanitizer, $normalize_newline) { | |
return $length_checker( | |
$normalize_newline( | |
$sanitizer($value), null, 50)); | |
}, | |
)); | |
// プロパティ形式 | |
$user->number = '1'; | |
$user->name = 'Tanaka'; | |
echo '<pre>'; | |
var_dump($user->number); // int(1) | |
var_dump($user->name); // string(6) "Tanaka" | |
echo '</pre>'; | |
// メソッド形式 | |
$user->number('2')->name('Suzuki'); | |
echo '<pre>'; | |
var_dump($user->number()); // int(2) | |
var_dump($user->name()); // string(6) "Suzuki" | |
echo '</pre>'; | |
// フィルタで制御コードをサニタイズ(←言うな) | |
$user->name("Holy\x00\x00\x00"); // NULL byte | |
echo '<pre>'; | |
var_dump($user->name()); // string(4) "Holy" | |
echo '</pre>'; | |
// フィルタで改行コードを正規化 | |
$user->comment("Hoge\r\nFuga\r\nPiyo\r\n"); // CR+LF newline | |
echo '<pre>'; | |
var_dump($user->comment()); // string(15) "Hoge\nFuga\nPiyo\n" | |
echo '</pre>'; | |
// 未定義プロパティへのアクセス | |
echo '<pre>'; | |
try { | |
$user->age = '30'; | |
} catch (\InvalidArgumentException $e) { | |
echo $e->getMessage(); // The property "age" does not exists. | |
} | |
echo '</pre>'; | |
// フィルタでチェック | |
echo '<pre>'; | |
try { | |
$user->number = '-1'; | |
} catch (\InvalidArgumentException $e) { | |
echo $e->getMessage(); // The property "number" is not valid : invalid unsigned integer. | |
} | |
echo '</pre>'; | |
// フィルタでチェック | |
echo '<pre>'; | |
try { | |
$user->name('VeryVeryLongName'); | |
} catch (\InvalidArgumentException $e) { | |
echo $e->getMessage(); // The property "name" is not valid : character length must not greater than 10. | |
} | |
echo '</pre>'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Acme; | |
class FilterableAccessor | |
{ | |
private $attributes; | |
private $filters; | |
public function __construct(array $params = array()) | |
{ | |
$this->init($params); | |
} | |
public static function instance(array $params = array()) | |
{ | |
return new static($params); | |
} | |
public function init(array $params = array()) | |
{ | |
$this->attributes = array(); | |
$this->filters = array(); | |
if (!empty($params)) { | |
foreach ($params as $name => $filter) { | |
if (!is_callable($filter)) { | |
throw new \InvalidArgumentException( | |
sprintf('The property "%s" filter is not callable.', $name)); | |
} | |
$this->filters[$name] = $filter; | |
$this->attributes[$name] = null; | |
} | |
} | |
return $this; | |
} | |
public function __get($name) | |
{ | |
return $this->get_attribute($name); | |
} | |
public function __set($name, $value) | |
{ | |
return $this->set_attribute($name, $value); | |
} | |
public function __call($name, array $args) | |
{ | |
switch (count($args)) { | |
case 0: | |
return $this->get_attribute($name); | |
case 1: | |
return $this->set_attribute($name, $args[0]); | |
} | |
throw new \BadMethodCallException( | |
sprintf('Invalid method "%s"' , $name)); | |
} | |
private function set_attribute($name, $value) | |
{ | |
if (!array_key_exists($name, $this->attributes)) { | |
throw new \InvalidArgumentException( | |
sprintf('The property "%s" does not exists.', $name)); | |
} | |
if (isset($this->filters[$name])) { | |
$message = null; | |
$valid = true; | |
try { | |
$value = call_user_func($this->filters[$name], $value); | |
} catch (\Exception $e) { | |
$message = $e->getMessage(); | |
$valid = false; | |
} | |
if (!$valid) { | |
throw new \InvalidArgumentException( | |
sprintf('The property "%s" is not valid%s', $name, | |
(isset($message)) ? " : {$message}" : '.')); | |
} | |
} | |
$this->attributes[$name] = $value; | |
return $this; | |
} | |
private function get_attribute($name) | |
{ | |
if (!array_key_exists($name, $this->attributes)) { | |
throw new \InvalidArgumentException( | |
sprintf('The property "%s" does not exists.', $name)); | |
} | |
return $this->attributes[$name]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment