php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69220 _call, __construct, __set
Submitted: 2015-03-11 11:12 UTC Modified: 2015-03-13 07:22 UTC
From: cybermerlin at ya dot ru Assigned:
Status: Not a bug Package: Class/Object related
PHP Version: 5.6.6 OS: winx64_8.1
Private report: No CVE-ID: None
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
31 + 21 = ?
Subscribe to this entry?

 
 [2015-03-11 11:12 UTC] 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;
			}
		}
	}


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-03-11 20:17 UTC] requinix@php.net
-Status: Open +Status: Feedback
 [2015-03-11 20:17 UTC] 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-12 06:28 UTC] cybermerlin at ya dot ru
-Status: Feedback +Status: Open
 [2015-03-12 06:28 UTC] 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-12 10:52 UTC] requinix@php.net
-Status: Open +Status: Not a bug
 [2015-03-12 10:52 UTC] requinix@php.net
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.
 [2015-03-12 12:21 UTC] cybermerlin at ya dot ru
But what about question 2)?
I see only hasField1 call
 [2015-03-13 02:03 UTC] requinix@php.net
As do I. hasField1 is the only method that does not exist/is not accessible, and it is only called once. What's the problem?
 [2015-03-13 06:22 UTC] cybermerlin at ya dot ru
I was expecting at least the message of two calls: setField1 and hasField1.
why there is no information about the call setField1?
 [2015-03-13 07:11 UTC] requinix@php.net
http://php.net/manual/en/language.oop5.overloading.php#object.call
>__call() is triggered when invoking inaccessible methods in an object context.
 [2015-03-13 07:22 UTC] cybermerlin at ya dot ru
oh, thanks. I'm sorry
I have no more questions
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 18 03:01:28 2024 UTC