I'm working with some existing code, specifically the JQuery File Upload Plugin. There is one large class and within that there are some functions i'm trying to customize. Problem is there are a few lines of code that make no sense to me.
protected function get_file_object($file_name) {
//whole bunch of code is here that generates an object file file size
//and other information related to the image that was in the array.
//removed the code to be concise, just know it returns an object.
return $file;
}
protected function get_file_objects() {
return array_values(
array_filter(
array_map(
array($this, 'get_file_object'),
scandir($this->options['upload_dir'])
)));
}
Okay, so what I don't understand is what is going on inside array_map. I know array map takes a callback and then an array as a arguments. scandir grabs an array from a directory.
Its the callback that makes no sense to me. I looked at the syntax for the array() function on the php documentation and it didn't say anything about taking two arguments like this. obviously the second one is a function, that's in quotes? I understand what the code is doing just not how its doing it.
Is this some undocumented functionality?
The first argument of array_map is a callable one of the things that is a callable is an array where the first element represents the instance (or classname if the method is static) and the second the methodname. So array($this, 'get_file_object') is refering to the get_file_object of the current instance ($this is the current instance).
Related
In my screenshot below you can see I have a list of functions that run a routine, fairly in-depth routine.
Previously, I have ben repeating this routine in multiple classes, but now I would like to consolidate those multiple classes into one class and execute only one function, by passing a variable into that function to determine the output to return.
I know how to pass the variable into "one" function, but how can I pass the variable ($this_id) into my multiple functions below? Basically, whatever $this_id is from get_output($this_id); I want that same variable value to be carried over into the other $this_id functions. See screenshot...
I searched online and all answers I've seen show how to do this in a non static way, but I'm only familiar with calling things statically, really. I tried the obj way, but couldn't get it to work.
Example, execution...
$header = 'CustomTheme_output';
$header::get_output('header');
(please disregard any lose code, the code is what I have so far from trying multiple ways. private $id and __construct are from the online solutions I have been trying)
Could you please clue me in on how I can correctly achieve this? I would be sooo happy to get rid of all the repetitive code, folders and files I have! - Thanks!
Either you pass it directly into each method call:
public function foo($this_id) {
$this->bar($this_id);
}
Or you make it a class attribute, and simply ACCESS it from the various methods:
public function foo($this_id) {
$this->id = $this_id;
$this->bar();
}
public function bar() {
do_something($this->id);
}
I want to declare a series of functions and name them using the elements of an array. I do not want to put the functions into an array as anonymous functions, but want them declared and accessible by calling them directly with their unique names from the elements of the array.
I want something like the following but that actually works:
for ($i=0;$i<count($tables);$i++) {
function $tables[$i][form]() {
}
// do something;
}
The purpose of these functions is to add submenu pages to my wordpress plugin's admin page. The number of submenus and their names depends on records in a table in the DB.
Perhaps there is a better way to do this altogether. Please help. When I tried using anonymous functions in an array and referencing the array elements in the function parameter in the add_submenu function, I kept getting permission problems when going to the submenu pages.
The name of your function has to be a constant. But you can still call a function whose name is stored inside an array.
How to call a function with a dynamic name.
http://www.php.net/call_user_func_array
You can also use anonymous function, but this is a little bit more difficult.
http://www.php.net/manual/en/functions.anonymous.php
The eval function was the solution I was looking for:
for ($i=0;$i<count($tables);$i++) {
$a[] = $tables[$i][form];
}
// Loop through the names of the submenu pages creating the required functions
foreach ($a as $functionname)
eval('function '.$functionname.' () {
// All the function code goes here
}');
I found that not escaping in and out of php appropriately was causing most of my problems especially when trying to reference variables.
Thanks Barmar for pointing me to PHP: define functions with variable names
I've built a CMS for our company which has a huge number of functions, many of which are related to specific functionality which isn't used on most of the sites we use the CMS for. My goal, then, is to only include the script of a function when it's needed (when the function is called).
Right now I'm simply calling up each function as normal, requiring the file where the actual script of the function is located, and then calling a second function (the same name as the function, but with an underscore prefix) which contains the actual script of the function. For example:
function foo($a,$b) {
require_once "funtions-foo.php";
return _foo($a,$b);
}
This, however, seems a little repetitive to me. What I'm looking for is a way to either 'catch' a functions call and, if its name is in an array of 'included' functions, i'll include the correct file and call the function. For example:
catch_function_call($function,$arg1,$arg2) {
$array = array(
'foo' => 'functions-foo.php',
'bar' => 'functions-bar.php'
);
if($array[$function]) {
require_once $array[$function];
return $function($arg1,$arg2);
}
}
There I'm assuming the 'catch_function_call' function can somehow catch when a function is called. As I know of no such function, however, my second thought was to simply define each of the 'included' functions using variables. For example:
$array = array(
'foo' => 'functions-foo.php',
'bar' => 'functions-bar.php'
);
foreach($array as $function => $file) {
function $function($arg1,$arg2) {
$_function = "_".$function;
require_once $file;
return $_function($arg1,$arg2);
}
}
Of course, this gives me an error as I apparently can't use a variable in the name of a function when defining it. Any thoughts on how to get around this or other solutions for only including a function when it's needed?
You can use __call or __callStatic on an object or class, respectively, which would approximate the functionality you're looking for. You can read some explanation in this thread.
However there's no way to do this in the global function space.
Could this help: http://php.net/manual/en/function.create-function.php ?
Or maybe turn the design to OO and use __call().
Just use include_once before each needed function?
Have you considered grouping your functions into sets and storing them as static methods in objects. Then you can use the __autoload() function and stop worrying about when to include.
So:
class Rarely_Used{
static function foo(){}
static function bar(){}
}
class Only_for_Managers{
static function ohboy(){}
static function punish(){}
}
Why is including all the files such a big problem? As you probably are using APC heavy filesystem access won't be a problem. And apart from that, lookups in the function hash table obviously are slower if there are more functions, but still this most certainly will not be the bottleneck of your application.
I am using the __call magic within some of my mvc code to produce an autoloadable forms framework but I have ran into a problem I am hoping some one on here might have a work around for.
The __call magic takes two paramters: $methodName and $arguments. The arguments come back as an array of args which you called. Normally this function is called on methods you can do as such so feed these arguments into a function:
call_user_func_array($methodName, $arguments);
And this will propogate the methods signature with the arguments. I am trying to do something a little more complex. I am attempting to propogate a classes constructor the same way, through being able to send maybe a imploded comma deliminenated array of the arguments into the classes constructor or just sending the args in directly but both of these do not produce the required result. When I send an imploded array down into the constructor PHP thinks it's a string and when I send the array it thinks it's an array.
This is the code I am using atm:
public function __call($method, $args){
return $this->init_element(new $method($args['name'], $args['value'], $args['opts']));
}
What if I had 4 arguments to pass down though? Is there a way I can get it to dynamically fill the constructor just like you can for a function using call_user_func_array()?
I could use an attributes() function to do this but I would really like to be able to do it like I can with functions.
Thanks in advance,
Use PHP's reflection classes: (http://php.net/manual/en/class.reflectionclass.php)
$obj = new ReflectionClass( $classname );
then
$ins = $obj->newInstanceArgs( $arguments );
or
$ins = $obj->newInstance( );
I saw the following somewhere around the web:
call_user_func_array(array($obj, '__construct'), $args);
I'm trying to get my head around using Iterators effectively in PHP 5, and without a lot of decent examples on the net, it's proving to be a little difficult.
I'm trying to loop over a directory, and read all the (php) files within to search for defined classes. What I then want to do is have an associative array returned with the class names as keys, the the file paths as the values.
By using a RecursiveDirectoryIterator(), I can recurse through directories.
By passing this into a RecursiveIteratorIterator, I can retrieve the contents of the directory as a single dimensional iterator.
By then using a filter on this, I can filter out all the directories, and non-php files which will just leave me the files I want to consider.
What I now want to do is be able to pass this iterator into another iterator (not sure which would be suitable), such that when it loops over each entry, it could retrieve an array which it needs to combine into a master array.
It's a little complicated to explain, so here's a code example:
// $php_files now represents an array of SplFileInfo objects representing files under $dir that match our criteria
$php_files = new PhpFileFilter(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)));
class ClassDetector extends FilterIterator {
public function accept() {
$file = $this->current(); // get the current item, which will be an SplFileInfo object
// Match all the classes contained within this file
if (preg_match($regex, $file->getContents(), $match)) {
// Return an assoc array of all the classes matched, the class name as key and the filepath as value
return array(
'class1' => $file->getFilename(),
'class2' => $file->getFilename(),
'class3' => $file->getFilename(),
);
}
}
}
foreach (new ClassDetector($php_files) as $class => $file) {
print "{$class} => {$file}\n";
}
// Expected output:
// class1 => /foo.php
// class2 => /foo.php
// class3 => /foo.php
// class4 => /bar.php
// class5 => /bar.php
// ... etc ...
As you can see from this example, I'm kind of hijacking the accept() method for FilterIterator, which is completely incorrect usage I know - but I use it only as an example to demonstrate how I just want the one function to be called, and for it to return an array which is merged into a master array.
At the moment I'm thinking I'm going to have to use one of the RecursionIterators, since this appears to be what they do, but I'm not fond of the idea of using two different methods (hasChildren() and getChildren()) to achieve the goal.
In short, I'm trying to identify which Iterator I can use (or extend) to get it to pass over a single-dimensional array(?) of objects, and get it to combine the resulting array into a master one and return that.
I realise that there are several other ways around this, ala something like:
$master = array();
foreach($php_files as $file) {
if (preg_match($regex, $file->getContents(), $match)) {
// create $match_results
$master = array_merge($master, $match_results);
}
}
but this defeats the purpose of using Iterators, and it's not very elegant either as a solution.
Anyway, I hope I've explained that well enough. Thanks for reading this far, and for your answers in advance :)
Right, I managed to get my head around it eventually. I had to use a Recursive iterator because the input iterator is essentially generating child results, and I extended IteratorIterator which already had the functionality to loop over an Iterator.
Anyways, here's a code example, just in case this helps anyone else. This assumes you've passed in an array of SplFileInfo objects (which are the result of a DirectoryIterator anyway).
class
ClassMatcher
extends
IteratorIterator
implements
RecursiveIterator
{
protected $matches;
public function hasChildren() {
return preg_match_all(
'#class (\w+)\b#ism',
file_get_contents($this->current()->getPathname()),
$this->matches
);
}
public function getChildren() {
$classes = $this->matches[1];
return new RecursiveArrayIterator(
array_combine(
$classes, // class name as key
array_fill(0, count($classes), $this->current()->getPathname()) // file path as value
)
);
}
}
I once did something similar. The source is right here and I believe is easily understandable. If you have any problem with it please let me know.
The main idea is to extend SplFileInfo and then use RecursiveIteratorIterator::setInfoClass($className); in order to obtain information about the source code. A Filter for parsing only PHP files could be nice though I decided back then to filter them by extension in the main loop.