-
Notifications
You must be signed in to change notification settings - Fork 0
/
replace_dynamic_receptor_with_dynamic_method_definition.php
113 lines (95 loc) · 3.33 KB
/
replace_dynamic_receptor_with_dynamic_method_definition.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?php
require_once('assert.php');
/*Replace Dynamic Receptor with Dynamic Method Definition
*
*This still uses a dynamic receptor because of PHP's current
*inability to add true class methods on the fly,
*however by limiting what the dynamic receptor will handle,
*we effectively remove the smell this particular refactoring addresses,
*which is a difficulty maintaining which method calls are handled
*by the dynamic receptor vs which are handled by explicitly defined
*class methods
*
*For the sake of exercise, PersonAfterTranslated follows as close to the version in
*the Ruby edition as possible, however in reality it is just as
*effective and likely more memory efficient
*(http://www.phpclasses.org/blog/post/187-The-Secret-PHP-Optimization-of-version-54.html)
*to simply handle the limitation logic in the __get method based on an
*explicitly declared property which contains the available "dynamic methods"
*as outlined in the PersonAfterAdapted class
*
*/
class PersonBefore
{
public function is_empty($property)
{
return empty($this->$property);
}
public function __call($method, $args)
{
return $this->is_empty(preg_replace('/^empty\_/', '', $method));
}
}
$before = new PersonBefore();
assert($before->empty_name());
assert($before->empty_age());
assert($before->empty_phone_number());
//will continue to work for anything empty_*
//over time this can become difficult to manage if
// you ever explicitly define any empty_* methods
class PersonAfterTranslated
{
private $additional_functions = array();
private function properties_with_empty_predicate(array $properties)
{
$self = $this;
foreach($properties as $property)
{
$method = 'empty_'.$property;
$this->$method = function() use ($self, $property) {
return empty($self->$property);
};
}
}
public function __construct()
{
$this->properties_with_empty_predicate(array('name', 'age'));
}
public function __call($method, $args)
{
if ($this->{$method} instanceof Closure)
return call_user_func_array($this->{$method},$args);
throw new Exception('Call to undefined method '.__CLASS__.'::'.$method);
}
}
$after = new PersonAfterTranslated();
assert($after->empty_name());
assert($after->empty_age());
try{
$after->empty_phone_number();
assert(false, 'empty_phone_number should not be available on PersonAfterTranslated class');
}catch(Exception $e){ }
class PersonAfterAdapted
{
private $properties_with_empty_predicate = array('name', 'age');
private function is_empty($property)
{
return (!property_exists($this, $property) or empty($this->$property));
}
public function __call($method, $args)
{
if (in_array($property = preg_replace('/^empty_/', '', $method), $this->properties_with_empty_predicate))
return $this->is_empty($property);
throw new Exception('Call to undefined method '.__CLASS__.'::'.$method);
}
}
$after = new PersonAfterAdapted();
assert($after->empty_name());
assert($after->empty_age());
try{
$after->empty_phone_number();
assert(false);
}catch(Exception $e){ }
global $fail_count;
echo ($fail_count === 0) ?
'All assertions passed' : "$fail_count assertions failed";