I'd like to call a static function of class A, in class B by passing it via Start.php as a parameter, but without including the ClassA.php into ClassB.php.
The reason I want to do this is because I have some client-specific logic, that i want to separate and call centrally and only when I really need it (index.php) without including it in the other files.
Let's say I've 3 files.
Start.php, ClassA.php, ClassB.php
ClassA.php
class A {
public static function foo($param) {
// do some special logic
// and return either true or false
}
}
ClassB.php
class B {
function bar($var, $func) {
foreach($var as $v) {
if($func($var)) {
echo 'OK';
}
}
}
}
Start.php
require_once('ClassA.php');
require_once('ClassB.php');
class Start() {
function init() {
$b = new B();
$test = array(1,2,3,4,5);
$b->bar($test, ['A', 'foo']);
}
}
Start::init();
So, Start depends on ClassA and ClassB, that's OK.
But I don't want ClassB to depend on ClassA.
When I do it like this, I get error, saying that Class A cannot be found.
Is that possible? Is that even considered as good practice?
I think you're going about this the wrong way. In fact, the syntax you're using here has been changed in PHP7. Let's clean this up
class B {
function bar($var, $func) {
$method = $func[0] . '::' . $func[1];
foreach($var as $v) {
if(call_user_func($method, $v)) {
echo 'OK';
}
}
}
}
So what we're doing here is we're telling PHP to use the static function in your array and then directly calling it using the precise call_user_func function (this is considered a best practice). Please note that I assumed that your call will always be static but it will work with any class you pass in that way.
You can see this in action here https://3v4l.org/WpeiD
I had to provide the full namespace to the function call, so:
$b->bar($test, ['my\\namespace\\A', 'foo']);
Related
I have the following:
class bar {
function __construct(){
// set $a_from_fire to $a from fire() method arg
// set $b_from_fire to $b from fire() method arg
}
}
class foo extends bar {
function fire ($a, $b){
}
}
I need to set $a_from_fire and $b_from_fire using the arguments from foo->fire()
So if I do this:
$test = new foo;
$test->fire(1, 2);
These vars will be set:
$a_from_fire == 1; // true
$b_from_fire == 2; // true
I don't think you can do it in any "correct" way. My first thought was to use __call, but that of course is only called for undefined functions.
And there's not really any way to dynamically rename the methods, unless you're already using RunKit. (not that I know of or could find anyway).
If it's purely for debug purposes, you could set your own class autoloader to pre-process the files, change the method names, and then use the __call magic method on your parent class.
spl_autoload_register(function($class){
$hackPath = '/home/_classes/'.$class;
if (!file_exists($hackPath)){
$realPath = '/home/classes/'.$class;
$file = file_get_contents($realPath);
$processedContent = //use regex or something to prepend all function names with an _.
file_put_contents($hackPath,$processedContent);
}
require_once $hackPath;
});
Then in your parent class
class parent {
public function __call($funcName,$arguments){
$this->myLogFunc($funcName,$arguments);
//since you prepended with an underscore
return call_user_func_array('_'.$funcName,$arguments);
}
This is a terrible way to do what you're asking, but it could work. The pre-processing of files might be slow, but you'd only need to do it if the originals changed (you can use filemtime to check if it's changed).
This isn't possible because __construct() is called when the object is first instantiated, so fire($a, $b) will always run after __construct()
If you just want to set the variables when fire() is called, simply do:
class bar {
protected $a_from_fire;
protected $b_from_fire;
}
class foo extends bar {
public function fire($a, $b) {
$this->a_from_fire = $a;
$this->b_from_fire = $b;
}
}
In PHP sometimes it would be nice if I could define a function or a class with a variable name like
$myfunctionname="test";
function $myfunctionname(){
//...
}
so it would create the function test()
or with classes too like:
$foo = bar;
class $foo {
// lots of complicated stuff
// ...
}
but this doesen't work. like this it would give parse errors!
Is there a solution to this?
(I know, this is not good practise, but just as a workaround, it would be handy)
EDIT: My actual problem:
I have a framework with a migration process where every migration step is in a separate php include file in a folder.
Each file contains only one migration class that contains the name of the include file.
Because the class has to have that certain name, I would like to create the name of the class to a generic name that is created by the filename constant __FILE__
Yes, you can, but I dont want you to.
$classname = "test";
eval("class {$classname}{ public function hello(){ echo \"hello!\"; } }");
$evil_class = new $classname();
$evil_class->hello(); // echo's "hello!"
now, if you don't mind me I'm going for a shower.
You can use a factory pattern:
class poly_Factory {
public function __construct() {
$class = 'poly';
return new $class();
}
}
If that is anything you want to get to.
http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
Scroll down to step 4, last part...
I know you did not ask for that, but what can your question be good for else?
No. This code throws a parse error on line 3 because of the $:
$foo = 'bar';
class $foo {
function hello() {
echo "World";
}
}
$mybar = new bar();
$mybar->hello();
Result:
Parse error: syntax error, unexpected T_VARIABLE, expecting T_STRING on line 3
And as Jan Dvorak pointed out in the comments: even if you figure out a way to do this, don't do this.
If you want to create a value object you can just use the stdClass builtin type.
$object = new stdClass;
$object->someValue = "Hello World";
echo $object->someValue;
See it in Action
If you want to assign methods then you have to use the magic __call function, here is how I would do it.
class AnonObject{
private $properties = array();
private $methods = array();
public function __get($property){
return array_key_exists($property, $this->properties)?$this->properties[$property]:null;
}
public function __set($property, $value){
if (!is_string($value) && is_callable($value)){
if ($value instanceof \Closure){
// bind the closure to this object's instance and static context
$this->methods[$property] = $value->bindTo($this,get_class($this));
} else {
// invokable objects
$this->methods[$property] = $value;
}
} else {
$this->properties[$property] = $value;
}
}
public function __call($method, $args){
if (array_key_exists($method, $this->methods)){
call_user_func_array($this->methods[$method], $args);
} else {
throw new RuntimeException("Method ".$method." does not exist on object");
}
}
}
See it In Action
Note, as stated by several other people this is bad practice. If the goal of this exercise is to compose the behavior of an instance of an object at runtime a more maintainable solution would be to use the Strategy Pattern
I came to know about mixins.So my doubt is, is it possible to use mixins in php?If yes then how?
Use Trait introduced in PHP 5.4
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld();
$o->sayHello();
?>
which prints Hello World!
http://php.net/manual/en/language.oop5.traits.php
This answer is obsolete as of PHP 5.4. See Jeanno's answer for how to use traits.
It really depends on what level of mixins you want from PHP. PHP handles single-inheritance, and abstract classes, which can get you most of the way.
Of course the best part of mixins is that they're interchangeable snippets added to whatever class needs them.
To get around the multiple inheritance issue, you could use include to pull in snippets of code. You'll likely have to dump in some boilerplate code to get it to work properly in some cases, but it would certainly help towards keeping your programs DRY.
Example:
class Foo
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
class Fizz
{
public function bar( $baz )
{
include('mixins/bar');
return $result;
}
}
It's not as direct as being able to define a class as class Foo mixin Bar, but it should get you most of the way there. There are some drawbacks: you need to keep the same parameter names and return variable names, you'll need to pass other data that relies on context such as func_get_args_array or __FILE__.
Mixins for PHP (PHP does not implement Mixins natively, but this library will help)
First google result for "php5 mixin": http://www.sitepoint.com/forums/php-application-design-147/ruby-like-mixins-php5-332491.html
First google result for "php mixin": http://www.advogato.org/article/470.html
Short answer: yes, but not natively (yet, evidently, as #mchl notes). Check those out.
Longer answer: if you're using runkit, checkout runkit_method_copy(): "Copies a method from class to another."
I based mixins functionality on the blog entry found at jansch.nl.
class Node
{
protected $__decorator_lookup = array();
public function __construct($classes = array())
{
foreach($classes as $class)
if (class_exists($class))
{
$decorator = new $class($this);
$methods = get_class_methods($decorator);
if (is_array($methods))
foreach($methods as $method)
$this->__decorator_lookup[strtolower($method)] = $decorator;
}
else
trigger_error("Tried to inherit non-existant class", E_USER_ERROR);
}
public function __get($name)
{
switch($name)
{
default:
if ($this->__decorator_lookup[strtolower($name)])
return $this->__call($name);
}
}
public function __call($method, $args = array())
{
if(isset($this->__decorator_lookup[strtolower($method)]))
return call_user_func_array(array($this->__decorator_lookup[strtolower($method)], $method), $args);
else
trigger_error("Call to undefined method " . get_class($this) . "::$method()", E_USER_ERROR);
}
public function __clone()
{
$temp = $this->decorators;
$this->decorators = array();
foreach($temp as $decorator)
{
$new = clone($decorator);
$new->__self = $this;
$this->decorators[] = $new;
}
}
}
class Decorator
{
public $__self;
public function __construct($__self)
{
$this->__self = $__self;
}
public function &__get($key)
{
return $this->__self->$key;
}
public function __call($method, $arguments)
{
return call_user_func_array(array($this->__self, $method), $arguments);
}
public function __set($key, $value)
{
$this->__self->$key = $value;
}
}
class Pretty extends Decorator
{
public function A()
{
echo "a";
}
public function B()
{
$this->b = "b";
}
}
$a = new Node(array("Pretty"));
$a->A(); // outputs "a"
$a->B();
echo($a->b); // outputs "b"
EDIT:
As PHP clone is shallow, added __clone support.
Also, bear in mind that unset WON'T work (or at least I've not managed to make it work) within the mixin. So - doing something like unset($this->__self->someValue); won't unset the value on Node. Don't know why, as in theory it should work. Funny enough unset($this->__self->someValue); var_dump(isset($this->__self->someValue)); will produce correctly false, however accessing the value from Node scope (as Node->someValue) will still produce true. There's some strange voodoo there.
I'm having some trouble creating a "super object" (similar to that of CodeIgniter) for a framework project I'm working on.
I want to make a super object that all of my other classes will run through. I did that, but it seems I am unable to use my super object's object in each of my classes without using the global keyword in every function.
I have something like this:
class a
{
function aa()
{
return 'class a';
}
}
class b
{
function bb()
{
$blah->a->aa();
}
}
class superobj
{
function loadClass($class)
{
$this->$class = new $class;
}
}
class blah extends superobj
{
function __construct()
{
$this->loadClass('a');
$this->loadClass('b');
}
}
$blah = new $blah;
So if I run this, I get an error because I can't access the aa method. If I put global $blah into the method bb, then it works (or at least in my real project it does, I dunno about this 5 second mockup hehe).
So is there any way to either make the $blah object global so that I don't need to reference it for every method, OR is there a better way to achieve what I am trying to do?
I'm not very good at OOP...
function bb() {
$blah->a->aa();
}
Indeed, as with any function, this function has no variables in its scope. $blah was neither passed to the function nor is it defined in the function, so it doesn't exist.
If your objects depend on an instance of blah to work, you should pass it to the constructor.
class b {
var $blah = null;
function __construct($blah) {
$this->blah = $blah;
}
function bb() {
$this->blah->foo();
}
}
$b = new b($instanceOfBlah);
Alternatively, use a static class that holds references to global objects and returns them on request (Registry pattern, like $blah = Registry::get('blah')).
Don't use global. Just don't.
I've got an Abstract PHP superclass, which contains code that needs to know which subclass its running under.
class Foo {
static function _get_class_name() {
return get_called_class();
//works in PHP 5.3.*, but not in PHP 5.2.*
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
}
class FooBar extends Foo {
}
Bar::other_code(); // i need 'Bar'
FooBar::other_code(); // i need 'FooBar'
This would work if I called the function get_called_class() -- however, this code is going to be run in PHP version 5.2.*, so that function is not available.
There's some custom PHP implementations of get_called_class() out there, but they all rely on going thru the debug_backtrack(), parsing a file name & line number, and running a regex (as the coder is not aware that PHP 5.2 has reflection) to find the class name. This code needs to be able to be run with php, ie. not only from a .php file. (It needs to work from a php -a shell, or an eval() statement.)
Ideally, a solution would work without requiring any code to be added to the subclasses… The only potential solution I can see though is adding the following code to each subclass, which is obviously a disgusting hack:
class FooBar extends Foo {
static function _get_class_name() {
return 'FooBar';
}
}
EDIT: Wait, this doesn't even seem to work. It would've been my last resort. Can anybody think of something similar to this solution that'd get me the required functionality. Ie., I'm willing to accept a solution that requires me to add one function or variable to each subclass telling it what its class name is. Unfortunately, it seems that calling self::_get_class_name() from the superclass calls the parent class' implementation, even if the subclass has overridden it.
In reality it is often helpful to know the actual called (sub)class when executing a superclass method, and I disagree that there's anything wrong with wanting to solve this problem.
Example, my objects need to know the class name, but what they do with that information is always the same and could be extracted into a superclass method IF I was able to get the called class name. Even the PHP team thought this was useful enough to include in php 5.3.
The correct and un-preachy answer, as far as I can tell, is that prior to 5.3, you have to either do something heinous (e.g. backtrace,) or just include duplicate code in each of the subclasses.
Working solution:
function getCalledClass(){
$arr = array();
$arrTraces = debug_backtrace();
foreach ($arrTraces as $arrTrace){
if(!array_key_exists("class", $arrTrace)) continue;
if(count($arr)==0) $arr[] = $arrTrace['class'];
else if(get_parent_class($arrTrace['class'])==end($arr)) $arr[] = $arrTrace['class'];
}
return end($arr);
}
This is not possible.
The concept of "called class" was introduced in PHP 5.3. This information was not tracked in previous versions.
As an ugly work-around, you could possibly use debug_backtrace to look into the call stack, but it's not equivalent. For instance, in PHP 5.3, using ClassName::method() doesn't forward the static call; you have no way to tell this with debug_backtrace. Also, debug_backtrace is relatively slow.
The PHP/5.2 alternative to late static binding that keeps duplicate code to the minimum while avoiding weird hacks would be to create one-liners on child classes that pass the class name as argument:
abstract class Transaction{
public $id;
public function __construct($id){
$this->id = $id;
}
protected static function getInstanceHelper($class_name, $id){
return new $class_name($id);
}
}
class Payment extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
class Refund extends Transaction{
public static function getInstance($id){
return parent::getInstanceHelper(__CLASS__, $id);
}
}
var_dump( Payment::getInstance(1), Refund::getInstance(2) );
object(Payment)#1 (1) {
["id"]=>
int(1)
}
object(Refund)#2 (1) {
["id"]=>
int(2)
}
The solution is:
get_class($this);
However, I don't know if this sentence works in static functions. Give it a try and tell me your feedback.
This hack includes the heinous use of debug_backtrace... not pretty, but it does the job:
<?php
function callerName($functionName=null)
{
$btArray = debug_backtrace();
$btIndex = count($btArray) - 1;
while($btIndex > -1)
{
if(!isset($btArray[$btIndex]['file']))
{
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
else
{
$lines = file($btArray[$btIndex]['file']);
$callerLine = $lines[$btArray[$btIndex]['line']-1];
if(!isset($functionName))
{
preg_match('/([a-zA-Z\_]+)::/',
$callerLine,
$matches);
}
else
{
preg_match('/([a-zA-Z\_]+)::'.$functionName.'/',
$callerLine,
$matches);
}
$btIndex--;
if(isset($matches[1]))
{
if(class_exists($matches[1]))
{
return $matches[1];
}
else
{
continue;
}
}
else
{
continue;
}
}
}
return $matches[1];
}
I have asked a question like this before, because I wanted a parent to have a factory method that was something like this
public static function factory() {
return new __CLASS__;
}
But it always returned the parent class, not the inherited one.
I was told that it is not possible without late static binding. It was introduced in PHP 5.3. You can read the documentation.
This function does the same job but works with instances too:
if (!function_exists('get_called_class')) {
function get_called_class() {
$bt = debug_backtrace();
/*
echo '<br><br>';
echo '<pre>';
print_r($bt);
echo '</pre>';
*/
if (self::$fl == $bt[1]['file'] . $bt[1]['line']) {
self::$i++;
} else {
self::$i = 0;
self::$fl = $bt[1]['file'] . $bt[1]['line'];
}
if ($bt[1]['type'] == '::') {
$lines = file($bt[1]['file']);
preg_match_all('/([a-zA-Z0-9\_]+)::' . $bt[1]['function'] . '/', $lines[$bt[1]['line'] - 1], $matches);
$result = $matches[1][self::$i];
} else if ($bt[1]['type'] == '->') {
$result = get_class($bt[1]['object']);
}
return $result;
}
}
<?php
class Foo {
private static $instance;
static function _get_class_name() {
return self::myNameIs();
}
static function other_code() {
//needs to know
echo self::_get_class_name();
}
}
class Bar extends Foo {
public static function myNameIs() {
self::$instance = new Bar();
return get_class(self::$instance);
}
}
class FooBar extends Foo {
public static function myNameIs() {
self::$instance = new FooBar();
return get_class(self::$instance);
}
}
Bar::other_code(); // i need 'Bar'
FooBar::other_code(); // i need 'FooBar'