public
Last active

Yii2, Object

  • Download Gist
Object.php
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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
<?php
/**
* Object class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
 
namespace yii\base;
 
/**
* Object is the base class that provides the *property* feature.
*
* @include @yii/base/Object.md
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Object
{
/**
* Constructor.
*/
public function __construct()
{
}
 
/**
* Returns the value of an object property.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$value = $object->property;`.
* @param string $name the property name
* @return mixed the property value, event handlers attached to the event,
* the named behavior, or the value of a behavior's property
* @throws Exception if the property is not defined
* @see __set
*/
public function __get($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
return $this->$getter();
} else {
throw new Exception('Getting unknown property: ' . get_class($this) . '.' . $name);
}
}
 
/**
* Sets value of an object property.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$object->property = $value;`.
* @param string $name the property name or the event name
* @param mixed $value the property value
* @throws Exception if the property is not defined or read-only.
* @see __get
*/
public function __set($name, $value)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
$this->$setter($value);
} elseif (method_exists($this, 'get' . $name)) {
throw new Exception('Setting read-only property: ' . get_class($this) . '.' . $name);
} else {
throw new Exception('Setting unknown property: ' . get_class($this) . '.' . $name);
}
}
 
/**
* Checks if the named property is set (not null).
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `isset($object->property)`.
*
* Note that if the property is not defined, false will be returned.
* @param string $name the property name or the event name
* @return boolean whether the named property is set (not null).
*/
public function __isset($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
// property is not null
return $this->$getter() !== null;
} else {
return false;
}
}
 
/**
* Sets an object property to null.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `unset($object->property)`.
*
* Note that if the property is not defined, this method will do nothing.
* If the property is read-only, it will throw an exception.
* @param string $name the property name
* @throws Exception if the property is read only.
*/
public function __unset($name)
{
$setter = 'set' . $name;
if (method_exists($this, $setter)) {
// write property
$this->$setter(null);
} elseif (method_exists($this, 'get' . $name)) {
throw new Exception('Unsetting read-only property: ' . get_class($this) . '.' . $name);
}
}
 
/**
* Calls the named method which is not a class method.
* If the name refers to a component property whose value is
* an anonymous function, the method will execute the function.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when an unknown method is being invoked.
* @param string $name the method name
* @param array $params method parameters
* @throws Exception when calling unknown method
* @return mixed the method return value
*/
public function __call($name, $params)
{
if ($this->canGetProperty($name, false)) {
$getter = 'get' . $name;
$func = $this->$getter;
if ($func instanceof \Closure) {
return call_user_func_array($func, $params);
}
}
throw new Exception('Unknown method: ' . get_class($this) . "::$name()");
}
 
/**
* Returns a value indicating whether a property is defined.
* A property is defined if there is a getter or setter method
* defined in the class. Note that property names are case-insensitive.
* @param string $name the property name
* @param boolean $checkVar whether to treat member variables as properties
* @return boolean whether the property is defined
* @see canGetProperty
* @see canSetProperty
*/
public function hasProperty($name, $checkVar = true)
{
return $this->canGetProperty($name, false) || $this->canSetProperty($name, false) || $checkVar && property_exists($this, $name);
}
 
/**
* Returns a value indicating whether a property can be read.
* A property can be read if the class has a getter method
* for the property name. Note that property name is case-insensitive.
* @param string $name the property name
* @param boolean $checkVar whether to treat member variables as properties
* @return boolean whether the property can be read
* @see canSetProperty
*/
public function canGetProperty($name, $checkVar = true)
{
return method_exists($this, 'get' . $name) || $checkVar && property_exists($this, $name);
}
 
/**
* Returns a value indicating whether a property can be set.
* A property can be written if the class has a setter method
* for the property name. Note that property name is case-insensitive.
* @param string $name the property name
* @param boolean $checkVar whether to treat member variables as properties
* @return boolean whether the property can be written
* @see canGetProperty
*/
public function canSetProperty($name, $checkVar = true)
{
return $checkVar && property_exists($this, $name) || method_exists($this, 'set' . $name);
}
 
/**
* Evaluates a PHP expression or callback under the context of this object.
*
* Valid PHP callback can be class method name in the form of
* array(ClassName/Object, MethodName), or anonymous function.
*
* If a PHP callback is used, the corresponding function/method signature should be
*
* ~~~
* function foo($param1, $param2, ..., $object) { ... }
* ~~~
*
* where the array elements in the second parameter to this method will be passed
* to the callback as `$param1`, `$param2`, ...; and the last parameter will be the object itself.
*
* If a PHP expression is used, the second parameter will be "extracted" into PHP variables
* that can be directly accessed in the expression.
* See [PHP extract](http://us.php.net/manual/en/function.extract.php)
* for more details. In the expression, the object can be accessed using `$this`.
*
* @param mixed $_expression_ a PHP expression or PHP callback to be evaluated.
* @param array $_data_ additional parameters to be passed to the above expression/callback.
* @return mixed the expression result
*/
public function evaluateExpression($_expression_, $_data_ = array())
{
if (is_string($_expression_)) {
extract($_data_);
return eval('return ' . $_expression_ . ';');
} else {
$_data_[] = $this;
return call_user_func_array($_expression_, $_data_);
}
}
 
/**
* Creates a new instance of the calling class.
*
* The newly created object will be initialized with the specified configuration.
*
* Extra parameters passed to this method will be used as the parameters to the object
* constructor.
*
* This method does the following steps to create a object:
*
* - create the object using the PHP `new` operator;
* - if [[Yii::objectConfig]] contains the configuration for the object class,
* it will be merged with the $config parameter;
* - initialize the object properties using the configuration passed to this method;
* - call the `init` method of the object if it implements the [[yii\base\Initable]] interface.
*
* For example,
*
* ~~~
* class Foo extends \yii\base\Object implements \yii\base\Initable
* {
* public $c;
* public function __construct($a, $b)
* {
* ...
* }
* public function init()
* {
* ...
* }
* }
*
* $model = Foo::newInstance(array('c' => 3), 1, 2);
* // which is equivalent to the following lines:
* $model = new Foo(1, 2);
* $model->c = 3;
* $model->init();
* ~~~
*
* @param array $config the object configuration (name-value pairs that will be used to initialize the object)
* @return \yii\base\Object the created object
* @throws Exception if the configuration is invalid.
*/
public static function newInstance($config = array())
{
$class = get_called_class();
 
if (($n = func_num_args()) > 1) {
$args = func_get_args();
if ($n === 2) {
$object = new $class($args[1]);
} elseif ($n === 3) {
$object = new $class($args[1], $args[2]);
} elseif ($n === 4) {
$object = new $class($args[1], $args[2], $args[3]);
} else {
// remove $config
array_shift($args);
$r = new \ReflectionClass($class);
$object = $r->newInstanceArgs($args);
}
} else {
$object = new $class;
}
 
if (isset(\Yii::$objectConfig[$class])) {
$config = array_merge(\Yii::$objectConfig[$class], $config);
}
 
foreach ($config as $name => $value) {
$object->$name = $value;
}
 
if ($object instanceof Initable) {
$object->init();
}
 
return $object;
}
}

Code formatting does not look proper to me.
I suggest the following improvements are worth to be made:

1) Open code segment brackets ({) have no single style: for the simple statements (if, foreach) you place them at the same line as statement start, while for class and function definition you place them at the new line. Why is that?
There is no need to place the open code segment bracket ({) at the new line: you just wasting the line of code, which does nothing. If the bracket is placed at the same line as its statement begins it separate the statement code well enough, so there is no need of new line.
Compare:


class MyClass
{
    public function myFunction()
    {
        if (rand()>1) {
            return ‘yes’;
        } else {
            return ‘no’;
        }
    }
}

and


class MyClass {
    public function myFunction() {
        if (rand()>1) {
            return ‘yes’;
        } else {
            return ‘no’;
        }
    }
}

2) Why using so long code section tabular? As I can see its length is equal to 8 spaces. This is too long and makes the class source code too wide. Just imagine 3 or 4 level nested blocks in this formatting: you will need entire 16:10 screen to hold it.
For “C” code style languages (like PHP) using single tab (4 spaces) as code block tabular is a standard. 4 spaces tabular length is just enough to separate blocks.

For the best code formatting style you may refer the Java code formatting conventions:
http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html

@klimov-paul

1) That's K&R style http://en.wikipedia.org/wiki/Indent_style#K.26R_style. Used in ZF2 and Symfony2 as well, going to be adopted by others as PSR-1 and PSR-2.
2) We're using tabs. github reformats these to spaces this way.

1) Java code convention is actually a late variation of K&R code style. I am not saying your code formatting is wrong. According the same wiki article, which you have referred, the pure K&R style, which places the class and function open brackets at the new line, has been risen cause the original C function definition restriction:

In old versions of the C programming language, the functions, however, were braced distinctly. The opening function brace of a function was placed on the line following after the declaration section and at the same indentation level as the declaration (header of the function). This is because in the original C language, argument types needed to be declared on the subsequent line (i. e., just after the header of the function), whereas when no arguments were necessary, the opening brace would not appear in the same line with the function declaration.

Actually there is no reason to place open bracket on class and function definition at the new line now (and for PHP).

See the different code style and decide for yourself, which is more proper and accurate. Do not follow old traditions blindly.

All I suggest here is making the code style more universal. I prefer the code block brackets to be placed at the same style always, instead of “same line” or “new line” depending on the statement nature. For me it is just easier. But if you dislike it so much, I can not force you…

2) Glad to hear it is only “\t” representation issue.

1) I think it's better to stick for ZF2 and Symfony2 style in this case rather than Java since these will be used as libraries for Yii2 often.

public function __get($name) {
    $getter = 'get' . $name;
    if (!method_exists($this, $getter)) {
        throw new Exception('Getting unknown property: ' . get_class($this) . '.' . $name);
    }
    
    return $this->$getter();
}
public function __isset($name) {
    $getter = 'get' . $name;
    return method_exists($this, $getter) ? 
        ($this->$getter() !== null) : null;
}

You declare to use PSR-2 standard. However the example code breaks it.
PSR-2 says: "Code MUST use an indent of 4 spaces, and MUST NOT use tabs for indenting.", see

https://github.com/pmjones/fig-standards/blob/psr-1-style-guide/proposed/PSR-2-advanced.md#24-indenting

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.