问题
I'm working on a class which needs to be accessible via static function calls as well as object methods. One thing I have found is that I'm duplicating logic across multiple functions.
Simplified example:
class Configurable{
protected $configurations = array();
protected static $static_configurations = array();
public function configure($name, $value){
// ...lots of validation logic...
$this->configurations[$name] = $value;
}
public static function static_configure($name, $value){
// ...lots of validation logic (repeated)...
self::$static_configurations[$name] = $value;
}
}
I've found a solution to this, but it feels really dirty:
class Configurable{
protected $configurations = array();
protected static $static_configurations = array();
public function configure($name, $value){
// ...lots of validation logic...
if (isset($this)){
$this->configurations[$name] = $value;
}
else{
self::$static_configurations[$name] = $value;
}
}
}
I need the static function as well so that I can set configurations throughout the application. Also, the nice thing with this technique is that I can use the same method names in both scopes.
Are there any problems with testing scope like this? Performance issues, forward compatibility issues, etc. It all works for me on PHP 5.2, and I don't need to support <5.
回答1:
The issue with the second method is that it will result in an error when error reporting is set to E_STRICT
. For example:
Strict standards: Non-static method Foo::bar() should not be called statically in /home/yacoby/dev/php/test.php on line 10
A point with PHP6 is that the E_STRICT errors are moved to E_ALL. In other words E_ALL will cover all errors including not allowing you to call non static methods statically.
An alternative method may be to move the validation logic to a static function. That way the non static function and the static function can call the validation logic.
回答2:
Static methods would require a different number of arguments than their objective counterpart - the additional argument would be an execution context. If there's no execution context, then it only makes sense to call it statically.
My preferred approach given that I'm building a library with multiple interfaces like this, is to create a static class and a dynamic class. Have one proxy the calls to the other. For example:
class DynamicClass {
protected $foo;
protected $bar;
public function baz($arg1) {
return StaticClass::bar($this->foo, $arg1);
}
public function zop($arg1, $arg2) {
return StaticClass::zop($this->foo, $this->bar, $arg1, $arg2);
}
// Context-less helper function
public function womp($arg1) {
return StaticClass::womp($arg1);
}
}
class StaticClass {
public static function baz(&$fooContext, $arg1) { ... }
public static function zop(&$fooContext, &$barContext, $arg1, $arg2) { ... }
public static function womp($arg1) { ... }
}
It's up to you exactly how you pass context to the static class - you'll have to do whatever makes sense for you. The work done in most functions should be pretty minor (if you're doing a lot, then you probably should be breaking the work up into smaller functions as a rule), and so should only require a handful of context arguments. Or you could create a full context array and pass that around everywhere (either populating it in DynamicClass
just before each call, or else track all DynamicClass
properties in that array so you can quickly & easily pass it around.
Though actually it looks like you might benefit from a Singleton design pattern. From what I can see, you're trying to create a global Configurable
, and also have the option to create individual local Configurable
s. With the singleton design pattern, you create a globally accessible version of a class that you can guarantee you only have one of (without breaking OOP design principles and having to rely on $_GLOBALS etc). For example:
class DynamicClass {
protected $foo;
protected $bar;
public function baz($arg1) { ... }
public function zop($arg1, $arg2) { ... }
public static function getSingleton() {
static $instance = null;
if ($instance === null) $instance = new DynamicClass();
return $instance;
}
}
No matter where in your code you are, you can get access to the same instance with DynamicClass::getSingleton()
. You also have the option of creating one-off non-singleton versions. You essentially get the best of both worlds while only having to write all your methods with dynamic access in mind exclusively.
回答3:
I don't find it so absurd to allow calling a method on an instance and statically as well. My case:
TestRecord::generateForm(); // Generate an empty form.
$test = new TestRecord( $primaryKey );
[...]
$test->generateForm(); // Generate an edit form with actual $test values.
Static side of my class deals with blank/new logics, while instance side means live data are used.
PHP 5.3 allows to achieve this by using __call
, __callStatic
and static::
:
public function __call( $name, $args )
{
if ( $name == 'generateForm' ) {
$this->fields = static::createFields(); // Action 1 : static.
$this->fillFields(); // Action 2 : instance.
static::renderForm( $this->fields ); // Action 3 : static.
}
}
public static function __callStatic( $name, $args )
{
if ( $name == 'generateForm' ) {
$fields = static::createFields(); // Action 1 : static.
// Action 2 : none.
static::renderForm( $fields ); // Action 3 : static.
}
}
Note: The static::
late binding qualifier is used because my 3 action methods (createFields
, fillFields
and rendreForm
) are implemented as protected
in the subclasses of this one, which is abstract
. This is possible because PHP let protected members be accessed in both directions: from base to subclass, but from subclass to superclass as well. Which is different from other OO languages, as far as I know.
回答4:
as in core php we use index.php?var=
, so to do the same thing in oop php what should we use.
来源:https://stackoverflow.com/questions/2347475/php-issetthis-and-using-the-same-object-method-in-a-static-and-object-context