Rev 60 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 60 | Rev 72 | ||
|---|---|---|---|
| 1 | <?php
|
1 | <?php
|
| 2 | 2 | ||
| 3 | namespace PointedEars\PHPX; |
3 | namespace PointedEars\PHPX; |
| 4 | 4 | ||
| 5 | /**
|
5 | /**
|
| 6 | * Base class providing generic wrappers for reading from and
|
6 | * Base class providing generic wrappers for reading from and
|
| 7 | * and writing to inaccessible properties.
|
7 | * and writing to inaccessible properties.
|
| 8 | *
|
8 | *
|
| 9 | * For each such property there must exist a public dynamically
|
9 | * For each such property there must exist a public dynamically
|
| 10 | * bound method (the PHP default) in the inheriting class whose
|
10 | * bound method (the PHP default) in the inheriting class whose
|
| 11 | * name is prefixed with 'get' (a <i>getter</i>, for read access)
|
11 | * name is prefixed with 'get' (a <i>getter</i>, for read access)
|
| 12 | * and/or with 'set' (a <i>setter</i>, for write access) followed
|
12 | * and/or with 'set' (a <i>setter</i>, for write access) followed
|
| 13 | * by the property name. (It is recommended to write the first
|
13 | * by the property name. (It is recommended to write the first
|
| 14 | * letter of the property name in the method name in uppercase.
|
14 | * letter of the property name in the method name in uppercase.
|
| 15 | * For runtime-efficiency, underscores in the property name are
|
15 | * For runtime-efficiency, underscores in the property name are
|
| 16 | * <em>not</em> converted to camel-case ["property_name" requires
|
16 | * <em>not</em> converted to camel-case ["property_name" requires
|
| 17 | * "getProperty_name", <em>not</em> "getPropertyName"].)
|
17 | * "getProperty_name", <em>not</em> "getPropertyName"].)
|
| 18 | *
|
18 | *
|
| 19 | * The getter or setter would then access the non-public property
|
19 | * The getter or setter would then access the non-public property
|
| 20 | * whose name is the name of the accessed property prefixed with
|
20 | * whose name is the name of the accessed property prefixed with
|
| 21 | * underscore ('_'), called the <em>underscore property</em>.
|
21 | * underscore ('_'), called the <em>underscore property</em>.
|
| 22 | * It can make sure that the value of the underscore property is
|
22 | * It can make sure that the value of the underscore property is
|
| 23 | * of a specific type or within a specific range, i. e. perform
|
23 | * of a specific type or within a specific range, i. e. perform
|
| 24 | * automatic type conversion, normalize values that are out of
|
24 | * automatic type conversion, normalize values that are out of
|
| 25 | * range when the property is read, and reject/ignore attempts
|
25 | * range when the property is read, and reject/ignore attempts
|
| 26 | * to set unsuitable property values. This is particularly useful
|
26 | * to set unsuitable property values. This is particularly useful
|
| 27 | * with instances of model classes; see {@link AbstractModel} and
|
27 | * with instances of model classes; see {@link AbstractModel} and
|
| 28 | * {@link Model}.
|
28 | * {@link Model}.
|
| 29 | *
|
29 | *
|
| 30 | * Properties that do not have a getter are not available
|
30 | * Properties that do not have a getter are not available
|
| 31 | * from unprivileged context, and an exception is thrown
|
31 | * from unprivileged context, and an exception is thrown
|
| 32 | * when attempting to read from them there.
|
32 | * when attempting to read from them there.
|
| 33 | *
|
33 | *
|
| 34 | * Properties that do not have a setter are effectively
|
34 | * Properties that do not have a setter are effectively
|
| 35 | * <em>read-only</em> from unprivileged context, and
|
35 | * <em>read-only</em> from unprivileged context, and
|
| 36 | * an exception is thrown when attempting to write to them there.
|
36 | * an exception is thrown when attempting to write to them there.
|
| 37 | *
|
37 | *
|
| 38 | * @author Thomas 'PointedEars' Lahn
|
38 | * @author Thomas 'PointedEars' Lahn
|
| 39 | */
|
39 | */
|
| 40 | abstract class Base |
40 | abstract class Base |
| 41 | {
|
41 | {
|
| 42 | /**
|
42 | /**
|
| - | 43 | * Determines if a strict model is enforced.
|
|
| - | 44 | *
|
|
| - | 45 | * If <code>true</code>, all publicly accessible properties
|
|
| - | 46 | * must have a getter if readable, and a setter if writable.
|
|
| - | 47 | * Otherwise, accesses to non-existing public properties will be
|
|
| - | 48 | * forwarded to the correspnding underline property if no getter
|
|
| - | 49 | * or setter has been defined for it. The default is
|
|
| - | 50 | * <code>false</code> (non-strict) as that speeds up property
|
|
| - | 51 | * accesses and eases implementation considerably.
|
|
| - | 52 | *
|
|
| - | 53 | * @var bool
|
|
| - | 54 | */
|
|
| - | 55 | protected static $_strict = false; |
|
| - | 56 | ||
| - | 57 | /**
|
|
| 43 | * Retrieves a property value.
|
58 | * Retrieves a property value.
|
| 44 | *
|
59 | *
|
| 45 | * Automagically called when attempting to read data
|
60 | * Automagically called when attempting to read data
|
| 46 | * from inaccessible properties.
|
61 | * from inaccessible properties.
|
| 47 | *
|
62 | *
|
| 48 | * @param string $name
|
63 | * @param string $name
|
| 49 | * Property name
|
64 | * Property name
|
| 50 | * @throws InvalidArgumentException
|
65 | * @throws InvalidArgumentException
|
| 51 | * if the underscore property for the property
|
66 | * if the underscore property for the property
|
| 52 | * named <code><var>$name</var></code> does not exist
|
67 | * named <code><var>$name</var></code> does not exist
|
| 53 | * or has no getter
|
68 | * or has no getter
|
| 54 | * @return mixed
|
69 | * @return mixed
|
| 55 | * Return value of the property-specific getter
|
70 | * Return value of the property-specific getter
|
| 56 | */
|
71 | */
|
| 57 | public function __get ($name) |
72 | public function __get ($name) |
| 58 | {
|
73 | {
|
| 59 | $getter = 'get' . ucfirst($name); |
74 | $getter = 'get' . ucfirst($name); |
| 60 | if (method_exists($this, $getter)) |
75 | if (method_exists($this, $getter)) |
| 61 | {
|
76 | {
|
| 62 | return $this->$getter(); |
77 | return $this->$getter(); |
| 63 | }
|
78 | }
|
| 64 | 79 | ||
| 65 | if (property_exists($this, "_$name")) |
80 | if (property_exists($this, "_$name")) |
| 66 | {
|
81 | {
|
| - | 82 | $class = get_class($this); |
|
| - | 83 | if ($class::$_strict) |
|
| - | 84 | {
|
|
| 67 | throw new \InvalidArgumentException("Property '{$name}' has no getter"); |
85 | throw new \InvalidArgumentException("Strict model: Property '{$name}' has no getter"); |
| - | 86 | }
|
|
| - | 87 | ||
| - | 88 | return $this->{"_$name"}; |
|
| 68 | }
|
89 | }
|
| 69 | 90 | ||
| 70 | throw new \InvalidArgumentException("No such property: '{$name}'"); |
91 | throw new \InvalidArgumentException("No such property: '{$name}'"); |
| 71 | }
|
92 | }
|
| 72 | 93 | ||
| 73 | /**
|
94 | /**
|
| 74 | * Sets a property value.
|
95 | * Sets a property value.
|
| 75 | *
|
96 | *
|
| 76 | * Automagically called when attempting to write data
|
97 | * Automagically called when attempting to write data
|
| 77 | * to inaccessible properties.
|
98 | * to inaccessible properties.
|
| 78 | *
|
99 | *
|
| 79 | * @param string $name
|
100 | * @param string $name
|
| 80 | * Property name
|
101 | * Property name
|
| 81 | * @param mixed $value
|
102 | * @param mixed $value
|
| 82 | * Property value
|
103 | * Property value
|
| 83 | * @throws InvalidArgumentException
|
104 | * @throws InvalidArgumentException
|
| 84 | * if the protected underscore property for the property
|
105 | * if the protected underscore property for the property
|
| 85 | * named <code><var>$name</var></code> does not exist
|
106 | * named <code><var>$name</var></code> does not exist
|
| 86 | * or has no setter (is read-only)
|
107 | * or has no setter (is read-only)
|
| 87 | * @return mixed
|
108 | * @return mixed
|
| 88 | * Return value of the property-specific setter
|
109 | * Return value of the property-specific setter
|
| 89 | */
|
110 | */
|
| 90 | public function __set ($name, $value) |
111 | public function __set ($name, $value) |
| 91 | {
|
112 | {
|
| 92 | $setter = 'set' . ucfirst($name); |
113 | $setter = 'set' . ucfirst($name); |
| 93 | if (method_exists($this, $setter)) |
114 | if (method_exists($this, $setter)) |
| 94 | {
|
115 | {
|
| 95 | return $this->$setter($value); |
116 | return $this->$setter($value); |
| 96 | }
|
117 | }
|
| 97 | 118 | ||
| 98 | if (property_exists($this, "_$name")) |
119 | if (property_exists($this, "_$name")) |
| 99 | {
|
120 | {
|
| - | 121 | $class = get_class($this); |
|
| - | 122 | if ($class::$_strict) |
|
| - | 123 | {
|
|
| 100 | throw new \InvalidArgumentException("Property '{$name}' has no setter"); |
124 | throw new \InvalidArgumentException("Strict model: Property '{$name}' has no setter"); |
| - | 125 | }
|
|
| - | 126 | ||
| - | 127 | $this->{"_$name"} = $value; |
|
| - | 128 | return $value; |
|
| 101 | }
|
129 | }
|
| 102 | 130 | ||
| 103 | throw new \InvalidArgumentException("No such property: '{$name}'"); |
131 | throw new \InvalidArgumentException("No such property: '{$name}'"); |
| 104 | }
|
132 | }
|
| 105 | }
|
133 | }
|