Bug #69220 [Opn->Nab]: _call, __construct, __set
| From: | requinix@php.net | Date: | Thu, 12 Mar 2015 10:52:01 +0000 |
| Subject: | Bug #69220 [Opn->Nab]: _call, __construct, __set | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-191350@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=69220&edit=1
ID: 69220
Updated by: requinix@php.net
Reported by: cybermerlin at ya dot ru
Summary: _call, __construct, __set
-Status: Open
+Status: Not a bug
Type: Bug
Package: Class/Object related
Operating System: winx64_8.1
PHP Version: 5.6.6
Block user comment: N
Private report: N
New Comment:
get_class_vars() will not show you private variables unless you call it from within their defining
class. While you could call it from within the child in abstract "helper" method, the
parent would still not have access to the variable and would thus error.
On the other hand, protected variables *are* accessible from parents so you could make the variable
protected and the code would work.
Previous Comments:
------------------------------------------------------------------------
[2015-03-12 06:28:07] cybermerlin at ya dot ru
<?php
abstract class Base{
protected $funcs = [];
public function __call($name, $args) {
error_log("\t>>>Call [$name](". json_encode($args). ") \r\n",3,
LOGFILE);
if (method_exists($this, $name)) {
return call_user_func_array($this->$name, $args);
} else if (isset($this->funcs[ $name ])) {
return call_user_func_array($this->funcs[ $name ], $args);
} else {
throw new Exception("Method <$name> is not accessible.");
}
}
/**
* @param array $args
*
* @example new Base(["field1"=>"value1",
"field2"=>"value2"])
* if u need override __constructor, then use:
* __construct($args){
* ..
* parent::__construct($args);
* }
*/
public function __construct($args = []) {
$nameClass = get_class($this);
#region auto create methods 'hasField' for everyone field\property (if there exists
getter or setter)
$properties = get_class_vars($nameClass);
$keys = array_keys($properties);
foreach ($keys AS $k) {
if ((method_exists($this, "set" . ucfirst($k)) || method_exists($this, "get"
. ucfirst($k)))
&& (!method_exists($this, "has" . ucfirst($k) &&
!isset($this->funcs["has" . ucfirst($k)])))
) {
$v = &$this->$k;
$this->funcs["has" . ucfirst($k)] = function () use (&$v) { return !empty($v);
};
}
}
#endregion
foreach ($args as $k => $v) {
if (property_exists($nameClass, $k)) {
$this->$k = $v;
}
}
}
}
class Test extends Base{
private $field1;
public function setField1($v){$this->field1 = $v;}
public function getField1(){return $this->field1;}
}
$test = new Test();
$test->setField1(23423);
echo $test->hasField1();
?>
------------------------------------------------------------------------
[2015-03-11 20:17:22] requinix@php.net
Thank you for this bug report. To properly diagnose the problem, we
need a short but complete example script to be able to reproduce
this bug ourselves.
A proper reproducing script starts with <?php and ends with ?>,
is max. 10-20 lines long and does not require any external
resources such as databases, etc. If the script requires a
database to demonstrate the issue, please make sure it creates
all necessary tables, stored procedures etc.
Please avoid embedding huge scripts into the report.
What you have there is complicated and incomplete. I tried a brief test script that includes
closures, references, and the constructor as are used in your class but I get the expected behavior.
------------------------------------------------------------------------
[2015-03-11 11:12:58] cybermerlin at ya dot ru
Description:
------------
1)
in __construct I wrote creator of methods hasSomeField for each field.
But if I tried to invoke it, then nothing happens.
2)
in __call I added writer to LOG name executing method, but in LOG exists only Exception for
'hasSomeField'.
Test script:
---------------
class Base{
protected $funcs = [];
public function __call($name, $args) {
error_log("\t>>>Call [$name](". json_encode($args). ") \r\n",3,
LOGFILE);
if (method_exists($this, $name)) {
return call_user_func_array($this->$name, $args);
} else if (isset($this->funcs[ $name ])) {
return call_user_func_array($this->funcs[ $name ], $args);
} else {
throw new Exception("Method <$name> is not accessible.");
}
}
/**
* @param array $args
*
* @example new Base(["field1"=>"value1",
"field2"=>"value2"])
* if u need override __constructor, then use:
* __construct($args){
* ..
* parent::__construct($args);
* }
*/
public function __construct($args = []) {
$nameClass = get_class($this);
#region auto create methods 'hasField' for everyone field\property (if there exists
getter or setter)
$properties = get_class_vars($nameClass);
$keys = array_keys($properties);
foreach ($keys AS $k) {
if ((method_exists($this, "set" . ucfirst($k)) || method_exists($this, "get"
. ucfirst($k)))
&& (!method_exists($this, "has" . ucfirst($k) &&
!isset($this->funcs["has" . ucfirst($k)])))
) {
$v = &$this->$k;
$this->funcs["has" . ucfirst($k)] = function () use (&$v) { return !empty($v);
};
}
}
#endregion
foreach ($args as $k => $v) {
if (property_exists($nameClass, $k)) {
$this->$k = $v;
}
}
}
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=69220&edit=1