How to call a callback function in a PHP class - php

I want to use array_filter to remove those items in an array whose value is equal to a specific character like '.' . To do, so I used the following code but I don't know how to pass the callback function to array_filter:
class Myclass(){
private function isPunc($var){
if($var=='.'){
return TRUE;
}else{
return FALSE;
}
public function myfunction($arr){
$arr = array_filter($arr,"isPunc");
}
}
Any idea how to solve this problem?

class Myclass(){
private function isPunc($var) {
if ($var=='.') {
return TRUE;
} else {
return FALSE;
}
}
public function myfunction($arr) {
$arr = array_filter($arr, array($this,'isPunc'));
}
}

use $arr = array_filter($arr, array($this, 'isPunc'));

array_filter() expects a Callable. This is an special internal type in PHP that can be on of four things:
a string with function name
an array with an object and method name as elements
an anonymous function
a functor (an object that implements __invoke)
In your case the second variant should work:
class FilterIsDot {
private function accept($element) {
if($element == '.'){
return TRUE;
}else{
return FALSE;
}
}
public function filter($array) {
return array_filter(
$array, array($this, 'accept')
);
}
}
$in = array('.', 'foo');
$filter = new FilterIsDot();
var_dump($filter->filter($in));
I would suggest a different approach avoiding array_filter(). SPL contains a FilterIterator class. You can extends this class:
class FilterIsDot extends FilterIterator {
public function __construct($arrayOrIterator) {
parent::__construct(
is_array($arrayOrIterator)
? new ArrayIterator($arrayOrIterator)
: $arrayOrIterator
);
}
public function accept() {
if($this->current() == '.') {
return TRUE;
}else{
return FALSE;
}
}
}
$in = array('.', 'foo');
$filter = new FilterIsDot($in);
var_dump(iterator_to_array($filter));
In this case the filter works on the fly. It is only used if the elements are actually accessed.

Related

PHP - How to call another function in a class dynamically

My PHP codes :
class FrontEndController extends BaseController {
public function Welcome()
{
return View::make('frontend.index');
}
public function Modules($param="")
{
/* convert parameter "$param" be a function */
$prefix = "Mod_"; // func prefix's
if (function_exists($this->$prefix.$param)) {
return $this->$prefix.$param();
}else
{
return $param;
}
}
protected function Mod_product(){
return "You are called CATEGORY";
}
protected function Mod_promo(){
return "You are called PROMO";
}
}
When i use FrontEndController->Modules('product'); i want it return value from Mod_product(), so also when I use FrontEndController->Modules('promo'); it will return value from Mod_promo(). How to do this?
Use method_exists:
$prefix = "Mod_"; // func prefix's
$function = $prefix.$param;
if ( method_exists( $this, $function ) ) {
return $this->$function();
} else {
return $param;
}
You have a few mistakes in your code:
function_exists does not work for classes, use method_exists instead.
if (function_exists($this->$prefix.$param)) {
This does not work either, use call_user_func instead.
return $this->$prefix.$param();
Corrected code:
class FrontEndController extends BaseController {
public function Welcome()
{
return View::make('frontend.index');
}
public function Modules($param="")
{
/* convert parameter "$param" be a function */
$prefix = "Mod_"; // func prefix's
$fcn = $prefix.$param; // save name of function
if (method_exists($this, $fcn)) {
return call_user_func(array($this, $fcn));
}else
{
return $param;
}
}
protected function Mod_product(){
return "You are called CATEGORY";
}
protected function Mod_promo(){
return "You are called PROMO";
}
}
Update your Modules function like this
$this->{$prefix . $params}();
Hope it helps :)
The Reflection Class is the ideal tool for this. It's a Swiss army knife for classes.
public function Modules($param="")
{
/* convert parameter "$param" be a function */
$prefix = "Mod_"; // func prefix's
$reflection = new ReflectionClass(__CLASS__);
if($reflection->hasMethod($prefix.$param)) {
return $this->$prefix.$param();
}else
{
return $param;
}
}

PHP array with default value for nonexisting indices

I love the Hash implementation of Ruby where you can initialize the Hash object with a default value. At the moment I'm struggling with implementing a similar object in PHP. This is my first (non-working) shot at this.
class DefaultArray extends ArrayObject {
protected $_defaultValue;
public function setDefault($defaultValue) {
$this->_defaultValue = $defaultValue;
}
public function offsetExists($index) {
return true;
}
public function offsetGet($index) {
if(!parent::offsetExists($index)) {
if(is_object($this->_defaultValue))
$default = clone $this->_defaultValue;
else
$default = $this->_defaultValue;
parent::offsetSet($index, $default);
}
return parent::offsetGet($index);
}
}
$da = new DefaultArray();
assert($da["dummy"] == null);
$da->setDefault = 1;
assert($da["dummy2"] == 1);
The second assertion will fail. Stepping through the code shows that offsetGet is called and the if clause is executed. Nevertheless any array value is null. Any ideas for alternative implementations?
I'm tired of writing
if(!isset($myarr['value']))
$myarr['value'] = new MyObj();
$myarr['value']->myVal=5;
instead of just writing
$myarr['value']->myVal=5;
$da->setDefault(1);
You can also use the __construct magic function:
class DefaultArray extends ArrayObject
{
public function __construct($value = null){
if(is_null($value))
{
$this->value = 'default';
} else {
$this->value = $value;
}
}
}
Try the magic methods __get.
class DefaultArray extends ArrayObject {
protected $_defaultValue;
public function setDefault($defaultValue) {
$this->_defaultValue = $defaultValue;
}
public function __get($index) {
return $this->offsetGet($index);
}
public function offsetGet($index) {
if(!parent::offsetExists($index)) {
if (is_object($this->_defaultValue)) {
$default = clone $this->_defaultValue;
} else {
$default = $this->_defaultValue;
}
parent::offsetSet($index, $default);
}
return parent::offsetGet($index);
}
}
Now you just need to use different keys as the read access will initialize that array items:
$da = new DefaultArray();
assert($da['foo'] == null);
$da->setDefault(1);
assert($da['bar'] == 1);
You could use my tiny library ValueResolver in this case, for example:
class DefaultArray extends ArrayObject
{
public function __construct($value = null){
$this->value = ValueResolver::resolve($value, 'default'); // returns 'default' if $value is null
}
}
and don't forget to use namespace use LapaLabs\ValueResolver\Resolver\ValueResolver;
There are also ability to typecasting, for example if your variable's value should be integer, so use this:
$id = ValueResolver::toInteger('6 apples', 1); // returns 6
$id = ValueResolver::toInteger('There are no apples', 1); // returns 1 (used default value)
Check the docs for more examples
Why so complicated?
function initVal($value) {
global $myarr;
if(!isset($myarr['value']))
$myarr['value'] = new MyObj();
}
Now you just have to call:
initVal('bla');
$myarr['bla']->bla = 'bla';
But I see, yours is much more neat.

Is this possible in PHP?

Consider the following PHP snippet:
<?php
class Is
{
function __get($key)
{
$class = __CLASS__ . '_' . $key;
if (class_exists($class) === true)
{
return $this->$key = new $class();
}
return false;
}
function Domain($string)
{
if (preg_match('~^[0-9a-z\-]{1,63}\.[a-z]{2,6}$~i', $string) > 0)
{
return true;
}
return false;
}
}
class Is_Domain
{
function Available($domain)
{
if (gethostbynamel($domain) !== false)
{
return true;
}
return false;
}
}
$Is = new Is();
var_dump($Is->Domain('google.com')); // true
var_dump($Is->Domain->Available('google.com')); // false
?>
Is it possible to call the Available() method like this (and still return solely true or false if the Available method is not called)?
var_dump($Is->Domain('google.com')->Available()); // false
If yes, how?
EDIT: Would this do the trick?
class Is
{
function __get($key)
{
// same as before
}
function Domain($string)
{
if (preg_match('~^[0-9a-z\-]{1,63}\.[a-z]{2,6}$~i', $string) > 0)
{
return (bool) $this->Domain;
}
return false;
}
}
class Is_Domain
{
function __toString()
{
return true;
}
function Available($domain)
{
if (gethostbynamel($domain) !== false)
{
return true;
}
return false;
}
}
Thanks in Advance!
PS: This snippet is truncated, so don't expect it to make it a lot of sense just by its own.
Essentially you want a method to return either a bool or an object based on whether a subsequent method call to the result is going to occur. I don't think this will be possible without some massive hack (e.g. reading the PHP file in yourself and looking ahead), and it shouldn't be because your objects shouldn't be worrying about the context in which they are used.
Instead you could get the first call to return an object which is relevant in both cases, e.g. DomainLookupResult, which has two methods e.g. Exists() and IsAvailable(). You could then do:
$result = $Is->Domain('google.com');
$isValid = $result->Exists();
$isAvaliable = $result->IsAvailable();
//or chaining:
$isValid = $Is->Domain('google.com')->Exists();
$isAvailable = $Is->Domain('google.com')->IsAvailable();
You can only chain method calls if they return an object!
This is because you can only call methods on objects.
The problem with your code is that the methods return a non object value, either true or false. And the problem is not in any way solved better by chaining methods. You should use that where its applicable. Like chaining many setters, NOT getters which the methods you want to use essentially is.
var_dump($Is->Domain->Available('google.com')); // false
//is the same as
$res = $Is->Domain;
$res = $res->Available('google.com'));
var_dump($res);
So you see the first res is a boolean true or false, and you can not call a method on that.
edit
This might be a "solution". Not a good solution though since this is better without chaining.
class Domain
{
public $domain;
function setDomain($domain) {
$this->domain = $domain;
return $this;
}
function isDomain($domain = null) {
if (is_string($domain)) {
$this->setDomain($domain);
}
$result = gethostbynamel($this->domain) !== false;
return new Result($this, $result);
}
function isValid() {
$result = (bool) preg_match('', $this->domain);
return new Result($this, $result)
}
}
class Result
{
public $result;
public $object;
public function __construct($object, $result)
{
$this->result = $result;
$this->object = $object;
}
public function __call($method, $arguments)
{
if (is_object($this->result)) {
return call_user_func_array(array($this->result, $method), $arguments);
}
if (!$this->result) {
return $this;
}
return call_user_func_array(array($this->object, $method), $arguments);
}
}
$domain = new Domain();
var_dump($domain->isValid('google.com')->isAvailable()->result);
/edit
This will solve your problem above.
var_dump($Is->Domain('validandfreedomain.com') && $Is_Domain->Available('validandfreedomain.com')); // true
If you desperately want to chain a method for this problem you could make it more like this.
class Domain
{
public $domain;
function setDomain($domain) {
$this->domain = $domain;
return $this;
}
function isAvailable() {
return gethostbynamel($this->domain) !== false;
}
function isValid() {
return (bool) preg_match('', $this->domain);
}
}
$domain = new Domain();
$result = $domain->setDomain('validandfreedomain.com')->isValid() && $domain->isAvailable();
It is possible, if your function returns an object, you can call its method, and so on (see method chaining). The only limitation is - as far as a I know - is that you cannot chain calls from an object created by new ( new Object()->method1()->method2() ).
As for your example, I see no point in using either the dynamic class, or method chaining stuff.

Custom foreach results for dynamic proxy class - magic methods?

I need to serialize a proxy class. The class uses __set and __get to store values in an array. I want the serialization to look like it is just a flat object. In other words, my class looks like:
class Proxy
{
public $data = array();
public function __get($name)
{
return $data[$name]
}
}
and I want a foreach loop to return all the keys and values in $data, when I say:
foreach($myProxy as $key)
Is this possible?
class Proxy implements IteratorAggregate
{
public $data = array();
public function __get($name)
{
return $data[$name];
}
public function getIterator()
{
$o = new ArrayObject($this->data);
return $o->getIterator();
}
}
$p = new Proxy();
$p->data = array(2, 4, 6);
foreach ($p as $v)
{
echo $v;
}
Output is: 246.
See Object Iteration in the PHP docs for more details.
You want to implement the SPL iterator interface
Something like this:
class Proxy implements Iterator
{
public $data = array();
public function __get($name)
{
return $data[$name]
}
function rewind()
{
reset($this->data);
$this->valid = true;
}
function current()
{
return current($this->data)
}
function key()
{
return key($this->data)
}
function next() {
next($this->data);
}
function valid()
{
return key($this->data) !== null;
}
}

PHP: How do I check if all public methods of two classes return the same values?

In effect, if I have a class c and instances of $c1 and $c2
which might have different private variable amounts but all their public methods return the same values I would like to be able to check that $c1 == $c2?
Does anyone know an easy way to do this?
You can also implement a equal($other) function like
<?php
class Foo {
public function equals($o) {
return ($o instanceof 'Foo') && $o.firstName()==$this.firstName();
}
}
or use foreach to iterate over the public properties (this behaviour might be overwritten) of one object and compare them to the other object's properties.
<?php
function equalsInSomeWay($a, $b) {
if ( !($b instanceof $a) ) {
return false;
}
foreach($a as $name=>$value) {
if ( !isset($b->$name) || $b->$name!=$value ) {
return false;
}
}
return true;
}
(untested)
or (more or less) the same using the Reflection classes, see http://php.net/manual/en/language.oop5.reflection.php#language.oop5.reflection.reflectionobject
With reflection you might also implement a more duck-typing kind of comparision, if you want to, like "I don't care if it's an instance of or the same class as long as it has the same public methods and they return the 'same' values"
it really depends on how you define "equal".
It's difficult to follow exactly what you're after. Your question seems to imply that these public methods don't require arguments, or that if they did they would be the same arguments.
You could probably get quite far using the inbuilt reflection classes.
Pasted below is a quick test I knocked up to compare the returns of all the public methods of two classes and ensure they were they same. You could easily modify it to ignore non matching public methods (i.e. only check for equality on public methods in class2 which exist in class1). Giving a set of arguments to pass in would be trickier - but could be done with an array of methods names / arguments to call against each class.
Anyway, this may have some bits in it which could be of use to you.
$class1 = new Class1();
$class2 = new Class2();
$class3 = new Class3();
$class4 = new Class4();
$class5 = new Class5();
echo ClassChecker::samePublicMethods($class1,$class2); //should be true
echo ClassChecker::samePublicMethods($class1,$class3); //should be false - different values
echo ClassChecker::samePublicMethods($class1,$class4); //should be false -- class3 contains extra public methods
echo ClassChecker::samePublicMethods($class1,$class5); //should be true -- class5 contains extra private methods
class ClassChecker {
public static function samePublicMethods($class1, $class2) {
$class1methods = array();
$r = new ReflectionClass($class1);
$methods = $r->getMethods();
foreach($methods as $m) {
if ($m->isPublic()) {
#$result = call_user_method($m->getName(), $class1);
$class1methods[$m->getName()] = $result;
}
}
$r = new ReflectionClass($class2);
$methods = $r->getMethods();
foreach($methods as $m) {
//only comparing public methods
if ($m->isPublic()) {
//public method doesn't match method in class1 so return false
if(!isset($class1methods[$m->getName()])) {
return false;
}
//public method of same name doesn't return same value so return false
#$result = call_user_method($m->getName(), $class2);
if ($class1methods[$m->getName()] !== $result) {
return false;
}
}
}
return true;
}
}
class Class1 {
private $b = 'bbb';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class2 {
private $a = 'aaa';
public function one() {
return 999;
}
public function two() {
return "bendy";
}
}
class Class3 {
private $c = 'ccc';
public function one() {
return 222;
}
public function two() {
return "bendy";
}
}
class Class4 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
public function three() {
return true;
}
}
class Class5 {
public function one() {
return 999;
}
public function two() {
return "bendy";
}
private function three() {
return true;
}
}
You can define PHP's __toString magic method inside your class.
For example
class cat {
private $name;
public function __contruct($catname) {
$this->name = $catname;
}
public function __toString() {
return "My name is " . $this->name . "\n";
}
}
$max = new cat('max');
$toby = new cat('toby');
print $max; // echoes 'My name is max'
print $toby; // echoes 'My name is toby'
if($max == $toby) {
echo 'Woohoo!\n';
} else {
echo 'Doh!\n';
}
Then you can use the equality operator to check if both instances are equal or not.
HTH,
Rushi
George: You may have already seen this but it may help: http://usphp.com/manual/en/language.oop5.object-comparison.php
When using the comparison operator (==), object variables are compared in a simple manner, namely: Two object instances are equal if they have the same attributes and values, and are instances of the same class.
They don't get implicitly converted to strings.
If you want todo comparison, you will end up modifying your classes. You can also write some method of your own todo comparison using getters & setters
You can try writing a class of your own to plugin and write methods that do comparison based on what you define. For example:
class Validate {
public function validateName($c1, $c2) {
if($c1->FirstName == "foo" && $c2->LastName == "foo") {
return true;
} else if (// someother condition) {
return // someval;
} else {
return false;
}
}
public function validatePhoneNumber($c1, $c2) {
// some code
}
}
This will probably be the only way where you wont have to modify the pre-existing class code

Categories