I want a class property to be reference to another class, not its object and then use this property to call the class's static methods.
class Database {
private static $log;
public static function addLog($LogClass) {
self::$log = $LogClass;
}
public static function log() {
self::$log::write(); // seems not possible to write it like this
}
}
any suggestions how i can accomplish this?
cause i have no reason making them objects, i want to use the classes for it.
Use the call_user_func function:
class Logger {
public static function write($string) {
echo $string;
}
}
class Database {
private static $log;
public static function addLog($LogClass) {
self::$log = $LogClass;
}
public static function log($string) {
call_user_func( array(self::$log, 'write'), $string );
}
}
$db = new Database();
$db->addLog('Logger');
$db->log('Hello world!');
Since you're seemingly only interested in one specific method/function (without further contracts/interfaces) you can write the code in a way that it doesn't matter whether it's a static method or an object method (...hm, object method...that doesn't sound right, what's the right name...) or a simple function.
class LogDummy {
public static function write($s) {
echo 'LogDummy::write: ', $s, "\n";
}
public function writeMe($s) {
echo 'LogDummy->writeMe: ', $s, "\n";
}
}
class Database {
private static $log=null;
public static function setLog($fnLog) {
self::$log = $fnLog;
}
public static function log($s) {
call_user_func_array(self::$log, array($s));
}
}
// static method
Database::setLog(array('LogDummy', 'write'));
Database::log('foo');
// member method
$l = new LogDummy;
Database::setLog(array($l, 'writeMe'));
Database::log('bar');
// plain old function
function dummylog($s) {
echo 'dummylog: ', $s, "\n";
}
Database::setLog('dummylog');
Database::log('baz');
// anonymous function
Database::setLog( function($s) {
echo 'anonymous: ', $s, "\n";
} );
Database::log('ham');
prints
LogDummy::write: foo
LogDummy->writeMe: bar
dummylog: baz
anonymous: ham
Related
So this is my mixin class:
class AisisCore_Loader_Mixins {
private $_classes;
private $_class_objects = array();
private $_methods = array();
public function __construct(){
$this->init();
}
public function init(){}
public function setup($class){
if(!is_array($class)){
throw new AisisCore_Loader_LoaderException('Object passed in must be of type $class_name=>$params.');
}
$this->_classes = $class;
$this->get_class_objects();
$this->get_methods();
}
public function get_class_objects(){
foreach($this->_classes as $class_name=>$params){
$object = new ReflectionClass($class_name);
$object_name = get_class($object);
$this->_class_objects[$object->name] = $object->newInstanceArgs($params);
}
}
public function get_methods(){
foreach($this->_class_objects as $class_object_name => $class_object){
$this->_methods[$class_object_name] = get_class_methods($class_object);
}
return $this->_methods;
}
public function __call($name, $param = null){
foreach($this->_methods as $class_name=>$methods){
foreach($methods as $method){
if($name === $method){
return $this->isParam($class_name, $method, $param);
}
}
}
throw new AisisCore_Loader_LoaderException("Method: " .$name.
" does not exist or it's access is not public");
}
private function isParam($class_name, $method, $param){
if($param != null){
call_user_func(array($class_name, $method), $param);
}else{
call_user_func(array($class_name, $method));
}
}
}
Pretty simple stuff, load a set of classes, allow you to call their functions and so on, but we have a new issue. It seems that classes passed into this are instantiated as static, thus their methods cannot use $this-> they are resorted to using self:: which is wrong.
Lets see an example of how this all works:
class BaseBridge extends AisisCore_Loader_Mixins{
public function __construct(){
parent::construct();
$this->setup(array('ClassB' => array()));
}
}
Lets Define ClassB
class ClassB{
public function __construct(){}
public function some_method(){
$this->_some_private_method();
}
private function _some_private_method(){}
}
Pretty basic stuff, so lets hook it all up in ClassA
class ClassA extends BaseBridge{
public function __construct(){
parent::__construct();
$this->some_method();
}
}
Quick Review: We have a core class, ClassA which extends BaseBridge which is our bridge class between one or more (meant to be used with more) classes that ClassA extends from. In this case were only extending from ClassB for simplicity.
Whats the issue? See, how in ClassB, were doing: $this->_some_private_method(); ya that's going to epically and catastrophically fail. Why? because I get the error: Using $this when not in object context which makes me so confused, so I change it to: self::$_some_private_method(); and it works like a charm.
Why? and what do I have to change or fix to make it so that $this can be used in a class being instantiated through the mixin class?
So with some slight modifications, I have managed to make this work. How ever I do not believe that a function with multiple arguments will work - Feed back appreciated.
class AisisCore_Loader_Mixins {
private $_classes;
private $_class_objects = array();
private $_methods = array();
public function __construct(){
$this->init();
}
public function init(){}
public function setup($class){
if(!is_array($class)){
throw new AisisCore_Loader_LoaderException('Object passed in must be of type $class_name=>$params.');
}
$this->_classes = $class;
$this->get_class_objects();
$this->get_methods();
}
public function get_class_objects(){
foreach($this->_classes as $class_name=>$params){
$object = new ReflectionClass($class_name);
$this->_class_objects[$object->name] = $object->newInstanceArgs($params);
}
}
public function get_methods(){
foreach($this->_class_objects as $class_object_name => $class_object){
$this->_methods[$class_object_name] = get_class_methods($class_object);
}
return $this->_methods;
}
public function __call($name, $param = null){
foreach($this->_methods as $class_name=>$methods){
foreach($methods as $method){
if($name === $method){
return $this->_is_param($class_name, $method, $param);
}
}
}
throw new AisisCore_Loader_LoaderException("Method: " .$name.
" does not exist or it's access is not public");
}
private function _is_param($class_name, $method, $param){
if($param != null){
$this->_param_is_array($class_name, $method, $param);
}else{
call_user_func(array($this->_class_objects[$class_name], $method));
}
}
private function _param_is_array($class_name, $method, $param){
if(is_array($param)){
call_user_func_array(array($this->_class_objects[$class_name], $method), $param);
}else{
call_user_func(array($this->_class_objects[$class_name], $method, $param));
}
}
}
Now functions inside of classes that are registered by this class can use $this->.
The issue is that I am not sure if multiple param based functions will actually work.
I understand that one can use interfaces to mandate the definition of a function, but I cannot find something that enables one to mandate function calls, such that e.g. if I create a class being a member of another class (via extends, etc), with a function, for that class to automatically ensure that mandatory functions are called in part with that function.
I mean, to clarify further:
class domain {
function isEmpty($input) {
//apply conditional logic and results
}
}
class test extends domain {
function addTestToDBTable($test) {
/**
* try to add but this class automatically makes it so that all rules of
* class domain must be passed before it can run
* - so essentially, I am no longer required to call those tests for each and
* every method
**/
}
}
Apologies if this appears incoherent by any means. Sure, it seems lazy but I want to be able to force context without having to concern abou
Update:
Okay, to clarify further: in PHP, if I extend and declare a __construct() for a child class, that child class will override the parent __construct(). I do not want this, I want the parent construct to remain and mandate whatever as it pleases just as the child class may do so also.
I guess it can be done in two different ways.
Aspect Oriented Programming
Have a look here https://github.com/AOP-PHP/AOP
Generate or write Proxy classes
A really simple example could be:
<?php
class A {
public function callMe() {
echo __METHOD__ . "\n";
}
}
class B extends A {
// prevents instantiation
public function __construct() {
}
public function shouldCallMe() {
echo __METHOD__ . "\n";
}
public static function newInstance() {
return new ABProxy();
}
}
class ABProxy {
private $b;
public function __construct() {
$this->b = new B();
}
public function __call($method, $args) {
$this->b->callMe();
return call_user_func_array(array($this->b, $method), $args);
}
}
// make the call
$b = B::newInstance();
$b->shouldCallMe();
// Outputs
// ------------------
// A::callMe
// B::shouldCallMe
Hopes this helps a bit.
Sounds like you want a Decorator.
See This answer for a detailed explanation on how to do it. Note that it does not require a class extension.
I would use a domain-validating decorator with some doc-block metaprogramming magic. But this is really a job for an entire library, which no doubt exists.
fiddle
<?php
class FooDomain {
public static function is_not_empty($input) {
return !empty($input);
}
}
class Foo {
/**
* #domain FooDomain::is_not_empty my_string
*/
public function print_string($my_string) {
echo $my_string . PHP_EOL;
}
}
$foo = new DomainValidator(new Foo());
$foo->print_string('Hello, world!');
try {
$foo->print_string(''); // throws a DomainException
} catch (\DomainException $e) {
echo 'Could not print an empty string...' . PHP_EOL;
}
// ---
class DomainValidator {
const DOMAIN_TAG = '#domain';
private $object;
public function __construct($object) {
$this->object = $object;
}
public function __call($function, $arguments) {
if (!$this->verify_domain($function, $arguments)) {
throw new \DomainException('Bad domain!');
}
return call_user_func_array(
array($this->object, $function),
$arguments
);
}
public function __get($name) {
return $this->object->name;
}
public function __set($name, $value) {
$this->object->name = $value;
}
private function verify_domain($function, $arguments) {
// Get reference to method
$method = new \ReflectionMethod($this->object, $function);
$domains = $this->get_domains($method->getDocComment());
$arguments = $this->parse_arguments(
$method->getParameters(),
$arguments
);
foreach ($domains as $domain) {
if (!call_user_func(
$domain['name'],
$arguments[$domain['parameter']]
)) {
return false;
}
}
return true;
}
private function get_domains($doc_block) {
$lines = explode("\n", $doc_block);
$domains = array();
$domain_tag = DomainValidator::DOMAIN_TAG . ' ';
foreach ($lines as $line) {
$has_domain = stristr($line, $domain_tag) !== false;
if ($has_domain) {
$domain_info = explode($domain_tag, $line);
$domain_info = explode(' ', $domain_info[1]);
$domains[] = array(
'name' => $domain_info[0],
'parameter' => $domain_info[1],
);
}
}
return $domains;
}
private function parse_arguments($parameters, $values) {
$ret = array();
for ($i = 0, $size = sizeof($values); $i < $size; $i++) {
$ret[$parameters[$i]->name] = $values[$i];
}
return $ret;
}
}
Output:
Hello, world!
Could not print an empty string...
I would like to write a generic method that refers to a generic class (but the same method) in php.
class A {
public static function Dox(){
}
}
class B {
public static function Dox(){
}
}
class C{
public static function Include($class){
$result = $class::Dox(); //instead of 2 methods => A::Dox and B::Dox
}
}
I get an error.
any suggestions?
include is a keyword. Rename your method to foo(), bar() or anything that is not a keyword.
e.g.
<?php
class A {
public static function Dox() { return 'A::Dox'; }
}
class B {
public static function Dox() { return 'B::Dox'; }
}
class C {
public static function foo($class) {
$result = $class::Dox();
echo 'result: ', $result, "\n";
}
}
foreach( array('A','B') as $c ) {
C::foo($c);
}
prints
result: A::Dox
result: B::Dox
Does call_user_func work?
class A {
public static function Dox() {
}
}
class B {
public static function Dox() {
}
}
class C {
public static function Include($class) {
$result = call_user_func(array($class, "Dox"));
}
}
Include keyword "spesific keyword".
Try it:
public static function IncludeXXX(){...}
I'm trying to add methods dynamically from external files.
Right now I have __call method in my class so when i call the method I want, __call includes it for me; the problem is I want to call loaded function by using my class, and I don't want loaded function outside of the class;
Class myClass
{
function__call($name, $args)
{
require_once($name.".php");
}
}
echoA.php:
function echoA()
{
echo("A");
}
then i want to use it like:
$myClass = new myClass();
$myClass->echoA();
Any advice will be appreciated.
Is this what you need?
$methodOne = function ()
{
echo "I am doing one.".PHP_EOL;
};
$methodTwo = function ()
{
echo "I am doing two.".PHP_EOL;
};
class Composite
{
function addMethod($name, $method)
{
$this->{$name} = $method;
}
public function __call($name, $arguments)
{
return call_user_func($this->{$name}, $arguments);
}
}
$one = new Composite();
$one -> addMethod("method1", $methodOne);
$one -> method1();
$one -> addMethod("method2", $methodTwo);
$one -> method2();
You cannot dynamically add methods to a class at runtime, period.*
PHP simply isn't a very duck-punchable language.
* Without ugly hacks.
You can dynamically add attributes and methods providing it is done through the constructor in the same way you can pass a function as argument of another function.
class Example {
function __construct($f)
{
$this->action=$f;
}
}
function fun() {
echo "hello\n";
}
$ex1 = new class('fun');
You can not call directlry $ex1->action(), it must be assigned to a variable and then you can call this variable like a function.
if i read the manual right,
the __call get called insted of the function, if the function dosn't exist
so you probely need to call it after you created it
Class myClass
{
function __call($name, $args)
{
require_once($name.".php");
$this->$name($args);
}
}
You can create an attribute in your class : methods=[]
and use create_function for create lambda function.
Stock it in the methods attribute, at index of the name of method you want.
use :
function __call($method, $arguments)
{
if(method_exists($this, $method))
$this->$method($arguments);
else
$this->methods[$method]($arguments);
}
to find and call good method.
What you are referring to is called Overloading. Read all about it in the PHP Manual
/**
* #method Talk hello(string $name)
* #method Talk goodbye(string $name)
*/
class Talk {
private $methods = [];
public function __construct(array $methods) {
$this->methods = $methods;
}
public function __call(string $method, array $arguments): Talk {
if ($func = $this->methods[$method] ?? false) {
$func(...$arguments);
return $this;
}
throw new \RuntimeException(sprintf('Missing %s method.'));
}
}
$howdy = new Talk([
'hello' => function(string $name) {
echo sprintf('Hello %s!%s', $name, PHP_EOL);
},
'goodbye' => function(string $name) {
echo sprintf('Goodbye %s!%s', $name, PHP_EOL);
},
]);
$howdy
->hello('Jim')
->goodbye('Joe');
https://3v4l.org/iIhph
You can do both adding methods and properties dynamically.
Properties:
class XXX
{
public function __construct($array1)
{
foreach ($array1 as $item) {
$this->$item = "PropValue for property : " . $item;
}
}
}
$a1 = array("prop1", "prop2", "prop3", "prop4");
$class1 = new XXX($a1);
echo $class1->prop1 . PHP_EOL;
echo $class1->prop2 . PHP_EOL;
echo $class1->prop3 . PHP_EOL;
echo $class1->prop4 . PHP_EOL;
Methods:
//using anounymous function
$method1 = function () {
echo "this can be in an include file and read inline." . PHP_EOL;
};
class class1
{
//build the new method from the constructor, not required to do it here by it is simpler.
public function __construct($functionName, $body)
{
$this->{$functionName} = $body;
}
public function __call($functionName, $arguments)
{
return call_user_func($this->{$functionName}, $arguments);
}
}
//pass the new method name and the refernce to the anounymous function
$myObjectWithNewMethod = new class1("method1", $method1);
$myObjectWithNewMethod->method1();
I've worked up the following code example and a helper method which works with __call which may prove useful. https://github.com/permanenttourist/helpers/tree/master/PHP/php_append_methods
I'm writing some PHP code in which one object (a "Container") keeps a pointer to another object (the "Contents"). The problem is that the contents needs to access methods or properties of the container.
Here's a simplified example of what I want to do:
class Container {
function __construct($type, $contents) {
$this->type = $type;
$this->contents = $contents;
}
function display() {
return $this->contents->display();
}
}
class Contents {
function __construct($stuff) {
$this->stuff = $stuff;
}
function display() {
return 'I am ' . $this->stuff . ' in '; // how to access Container here?
}
}
$item = new Container('a can', new Contents('Prince Albert'));
echo $item->display() . "\n";
// Displays: I am Prince Albert in
// Wanted: I am Prince Albert in a can
What's the right way to do this?
I've tried a couple of methods that work, but they feel wrong. For example:
Re-defined Contents::display() to take a parameter, which doesn't seem elegant:
function display($container) {
return 'I am ' . $this->stuff . ' in ' . $container->type;
}
In Contents::display(), I called debug_backtrace(true) to find out what called it, then access the object from the backtrace info. That feels kludgy/dangerous.
Is there a common solution for this kind of problem?
At all there are two common solution. The one is the first one you already mention
class A {
public function doSomething ($outer) { /* code */ }
}
where $outer is your container. Or you strictly bind the content objects to the container
class A {
private $outer;
public function __construct ($outer) {
$this->outer = $outer;
}
}
Try this:
class Container
{
protected $type;
protected $contents;
function __construct($type, Contents $contents)
{
$this->type = $type;
$this->contents = $contents;
$contents->setContainer($this);
}
function display()
{
return $this->contents->display();
}
public function getType()
{
return $this->type;
}
}
class Contents
{
/** #var Container */
protected $container;
function __construct($stuff)
{
$this->stuff = $stuff;
}
public function setContainer(Container $container)
{
$this->container = $container;
}
function display()
{
return 'I am '.$this->stuff.' in '.$this->container->getType(); // how to access Container here?
}
}
$item = new Container('a can', new Contents('Prince Albert'));
echo $item->display()."\n";
// Displays: I am Prince Albert in
// Wanted: I am Prince Albert in a can
And as advice: write public/protected/private for each method and variables, don't use public properties. If you don't know why, read this book: http://www.amazon.com/Objects-Patterns-Practice-Matt-Zandstra/dp/1590599098
With dependency injection, you would construct the Container first (not passing in a Contents):
class Container {
function __construct($type) {
Then, you would pass the Container to the Contents constructor:
class Contents {
function __construct($stuff, $container) {
Since the reference is mutual, you would have to call a setter on container:
class Container {
function setContents($contents)