Skip to content

Instantly share code, notes, and snippets.

@k-holy
Created April 6, 2012 08:55
Show Gist options
  • Save k-holy/2318279 to your computer and use it in GitHub Desktop.
Save k-holy/2318279 to your computer and use it in GitHub Desktop.
値のセット時に任意のフィルタを行うアクセサクラス
<?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>';
<?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