Skip to content

Instantly share code, notes, and snippets.

@samdark
Created May 30, 2012 12:22
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save samdark/2835928 to your computer and use it in GitHub Desktop.
Save samdark/2835928 to your computer and use it in GitHub Desktop.
Yii2, Object
<?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;
}
}
@klimov-paul
Copy link

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’;
        }
    }
}
  1. 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

@samdark
Copy link
Author

samdark commented May 31, 2012

@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.

@klimov-paul
Copy link

  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…

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

@samdark
Copy link
Author

samdark commented May 31, 2012

  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.

@knoxknox
Copy link

knoxknox commented Jun 2, 2012

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();
}

@knoxknox
Copy link

knoxknox commented Jun 2, 2012

public function __isset($name) {
    $getter = 'get' . $name;
    return method_exists($this, $getter) ? 
        ($this->$getter() !== null) : null;
}

@klimov-paul
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment