Related
I want to set a PHP variable: $custom, that will be used whenever it is defined.
But I want the output of this variable to be dependant of another variable: $v index, which is used inside while, which gets defined only during the while statement.
I don't know if it's possible to do, right now I have the following code, and for the way I defined $custom[3], it doesn't work.
Only $custom[1] and $custom[2] varibables will work, but they take into account only constant values or variable whose values were already set, so this isn't helpful.
Code:
<?php
$product[1]["price"]=10;
$product[2]["price"]=50;
$product[3]["price"]=70;
$custom[1] = 'Static HTML Block';
$custom[2] = 'Past Variable: '. $product[2]["price"] .'';
$custom[3] = 'Future Variable: '. $product[$v]["price"] .''; // I want this kind of definitoin
?>
<HTML>
// the following part will be used within an include, and shouldn't be modified ///
<?php
$v = 1;
while ($z <= 5) {
?>
<?= $custom[$v] ? $custom[$v] : '$' . $product[$v]["price"] ?>
<?php
$v = $v + 1;
$z = $z + 1;
}
?>
So basically, I want that on the third run (when v=3), that Future Variable: 70 will be the output.
The Rationale:
I want to use the latter code as a constant Include, that will serve as a template for all files. but on occasion, a certain file may require special changes that also require PHP code modification, so I will want to perform them within such specific file, that will affect the original include.
Edit 2:
More Simple example:
<?php
$product[1]["price"]=10;
$product[2]["price"]=50;
$custom[1] = 'I dont modify the PHP code';
$custom[2] = 'I DO mofiy the latter PHP code: '. $product[$v]["price"] .'';
?>
<HTML>
// the following part will be used within an include, and shouldn't be modified ///
<?php $v = 1; while ($v <= 5) { ?>
<?= $custom[$v] ?>
<p>
<?php $v = $v + 1; } ?>
// Expected Output:
//I dont modify the PHP code
//I DO mofiy the latter PHP code: 50
It's a little difficult to tell what you are trying to do, but I think you need to decouple your data from your presentation:
$product[1]['price']=10;
$product[2]['price']=50;
$product[3]['price']=70;
$custom[1] = 'Static HTML Block';
$custom[2] = 'Past Variable: %s';
$custom[3] = 'Future Variable: %s';
$v = 1;
while($z <= 5) {
$price = $product[ $v ]['price'];
printf($custom[ $v ], $price);
$v++;
$z++;
}
In this case, $custom stores the format, but doesn't actually contain the data. You call printf to output the desired format with whatever data you want to pass.
It's not clear to me what you're trying to accomplish, but if I had to guess, you want something known as templates (e.g. Twig), and your question might possibly be a XY problem. If you supply more context maybe someone might be able to help you further.
As to the matter at hand, you cannot define $custom[3] here since $v is not defined yet:
$custom[3] = 'Future Variable: '. $product[$v]["price"] .''; // I want this kind of definitoin
The closest you can get is to use eval (which is rarely recommended), or define a closure, even if it's awkward. Instead of a variable, you define a function:
$custom[3] = function($productData) {
return 'Future Variable: '. $productData["price"] .'';
}
The above you can do in an included file.
Then in the loop you check whether the custom object is a function, and if so, you call it and assign the return value to the reply.
The code below stays the same whatever the include - actually, it even works with the data you have now. Of course, it has to be a little more complicated.
while ($z <= 5) {
// If the $v-th item is customised
if (array_key_exists($v, $custom)) {
// Verify what it is
if (is_callable($custom[$v])) {
// if it's a function, call it.
$reply = $custom[$v]($product);
} else {
$reply = $custom[$v];
}
} else {
// Not customised, just print the price.
$reply = '$' . $product[$v]["price"];
}
print $reply;
Another more "templateish" solution is to define strings and then process them using e.g. preg_replace_callback in order to generate the desired HTML. In this case, too, you need to pass to the templater a fixed variable, such as $product[$v]. You need some kind of 'language' to indicate where you want elements of this fixed variables to be instantiated. Usually, square brackets or curly brackets, often doubled, are used:
$custom[3] = 'Future Variable: {{price}}';
At resolution time, preg_replace_callback may be made to replace {{price}} with the value in $product[$v]['price'], and so on (e.g. {{weight}}, {{description}}...).
(You can also do this server side and populate the HTML page via AJAX using client-side libraries such as Handlebars).
The quick and nasty way to make your own template is to create an array of keys => values and pass it to the where loops as your $custom variable.
While you're in the where(){} loop, use the php function extract to convert the array into $key = 'value' pairs. This is very similar to list without having having to define each variable manually.
http://php.net/manual/en/function.extract.php
Eg.
$data = [
'foo' => 'bar',
];
extract($data);
print $foo; // output: bar
Then after you extract, include your file. Make sure that you wrap this in output buffering mode ob_start() and put it in a function so your local variables can't be overriden.
This is how you make homebrew templating languages.
I'm probably missing something simple here, but I have this function for finding the factors of a number.
function factor($n){
$factors_array = array();
for ($x = 1; $x <= sqrt(abs($n)); $x++)
{
if ($n % $x == 0)
{
$z = $n/$x;
array_push($factors_array, $x, $z);
}
}
return $factors_array;
}
I then want to do something like this...
factor(120);
print_r($factors_array);
This give me nothing though. Any ideas on where I'm going wrong?
You aren't assigning the variable to the return value of the function. As far as the PHP interpreter is concerned, $factors_array only exists if you're inside the factor() function. Try this:
$factors_array = factor(120);
print_r($factors_array);
Then you can reuse $factors_array in other areas of the code.
Have a look at this page for an explanation of why this happens.
just try this:
print_r(factor(120));
Because you can't access $factors_array; outside the function, this is called scope of variable, usually, variables that defined inside function are not available outside, also, variables that are defined outside function are not available inside function...
Read more Variable scope ¶
I've recently stumbled over this code:
function xrange($min, $max)
{
for ($i = $min; $i <= $max; $i++) {
yield $i;
}
}
I've never seen this yield keyword before. Trying to run the code I get
Parse error: syntax error, unexpected T_VARIABLE on line x
So what is this yield keyword? Is it even valid PHP? And if it is, how do I use it?
What is yield?
The yield keyword returns data from a generator function:
The heart of a generator function is the yield keyword. In its simplest form, a yield statement looks much like a return statement, except that instead of stopping execution of the function and returning, yield instead provides a value to the code looping over the generator and pauses execution of the generator function.
What is a generator function?
A generator function is effectively a more compact and efficient way to write an Iterator. It allows you to define a function (your xrange) that will calculate and return values while you are looping over it:
function xrange($min, $max) {
for ($i = $min; $i <= $max; $i++) {
yield $i;
}
}
[…]
foreach (xrange(1, 10) as $key => $value) {
echo "$key => $value", PHP_EOL;
}
This would create the following output:
0 => 1
1 => 2
…
9 => 10
You can also control the $key in the foreach by using
yield $someKey => $someValue;
In the generator function, $someKey is whatever you want appear for $key and $someValue being the value in $val. In the question's example that's $i.
What's the difference to normal functions?
Now you might wonder why we are not simply using PHP's native range function to achieve that output. And right you are. The output would be the same. The difference is how we got there.
When we use range PHP, will execute it, create the entire array of numbers in memory and return that entire array to the foreach loop which will then go over it and output the values. In other words, the foreach will operate on the array itself. The range function and the foreach only "talk" once. Think of it like getting a package in the mail. The delivery guy will hand you the package and leave. And then you unwrap the entire package, taking out whatever is in there.
When we use the generator function, PHP will step into the function and execute it until it either meets the end or a yield keyword. When it meets a yield, it will then return whatever is the value at that time to the outer loop. Then it goes back into the generator function and continues from where it yielded. Since your xrange holds a for loop, it will execute and yield until $max was reached. Think of it like the foreach and the generator playing ping pong.
Why do I need that?
Obviously, generators can be used to work around memory limits. Depending on your environment, doing a range(1, 1000000) will fatal your script whereas the same with a generator will just work fine. Or as Wikipedia puts it:
Because generators compute their yielded values only on demand, they are useful for representing sequences that would be expensive or impossible to compute at once. These include e.g. infinite sequences and live data streams.
Generators are also supposed to be pretty fast. But keep in mind that when we are talking about fast, we are usually talking in very small numbers. So before you now run off and change all your code to use generators, do a benchmark to see where it makes sense.
Another Use Case for Generators is asynchronous coroutines. The yield keyword does not only return values but it also accepts them. For details on this, see the two excellent blog posts linked below.
Since when can I use yield?
Generators have been introduced in PHP 5.5. Trying to use yield before that version will result in various parse errors, depending on the code that follows the keyword. So if you get a parse error from that code, update your PHP.
Sources and further reading:
Official docs
The original RFC
kelunik's blog: An introduction to generators
ircmaxell's blog: What generators can do for you
NikiC's blog: Cooperative multitasking using coroutines in PHP
Co-operative PHP Multitasking
What is the difference between a generator and an array?
Wikipedia on Generators in general
This function is using yield:
function a($items) {
foreach ($items as $item) {
yield $item + 1;
}
}
It is almost the same as this one without:
function b($items) {
$result = [];
foreach ($items as $item) {
$result[] = $item + 1;
}
return $result;
}
The only one difference is that a() returns a generator and b() just a simple array. You can iterate on both.
Also, the first one does not allocate a full array and is therefore less memory-demanding.
simple example
<?php
echo '#start main# ';
function a(){
echo '{start[';
for($i=1; $i<=9; $i++)
yield $i;
echo ']end} ';
}
foreach(a() as $v)
echo $v.',';
echo '#end main#';
?>
output
#start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main#
advanced example
<?php
echo '#start main# ';
function a(){
echo '{start[';
for($i=1; $i<=9; $i++)
yield $i;
echo ']end} ';
}
foreach(a() as $k => $v){
if($k === 5)
break;
echo $k.'=>'.$v.',';
}
echo '#end main#';
?>
output
#start main# {start[0=>1,1=>2,2=>3,3=>4,4=>5,#end main#
yield keyword serves for definition of "generators" in PHP 5.5.
Ok, then what is a generator?
From php.net:
Generators provide an easy way to implement simple iterators without the overhead or complexity of implementing a class that implements the Iterator interface.
A generator allows you to write code that uses foreach to iterate over a set of data without needing to build an array in memory, which may cause you to exceed a memory limit, or require a considerable amount of processing time to generate. Instead, you can write a generator function, which is the same as a normal function, except that instead of returning once, a generator can yield as many times as it needs to in order to provide the values to be iterated over.
From this place: generators = generators, other functions (just a simple functions) = functions.
So, they are useful when:
you need to do things simple (or simple things);
generator is really much simplier then implementing the Iterator interface. other hand is, ofcource, that generators are less functional. compare them.
you need to generate BIG amounts of data - saving memory;
actually to save memory we can just generate needed data via functions for every loop iteration, and after iteration utilize garbage. so here main points is - clear code and probably performance. see what is better for your needs.
you need to generate sequence, which depends on intermediate values;
this is extending of the previous thought. generators can make things easier in comparison with functions. check Fibonacci example, and try to make sequence without generator. Also generators can work faster is this case, at least because of storing intermediate values in local variables;
you need to improve performance.
they can work faster then functions in some cases (see previous benefit);
None of the answers show a concrete example using massive arrays populated by non-numeric members. Here is an example using an array generated by explode() on a large .txt file (262MB in my use case):
<?php
ini_set('memory_limit','1000M');
echo "Starting memory usage: " . memory_get_usage() . "<br>";
$path = './file.txt';
$content = file_get_contents($path);
foreach(explode("\n", $content) as $ex) {
$ex = trim($ex);
}
echo "Final memory usage: " . memory_get_usage();
The output was:
Starting memory usage: 415160
Final memory usage: 270948256
Now compare that to a similar script, using the yield keyword:
<?php
ini_set('memory_limit','1000M');
echo "Starting memory usage: " . memory_get_usage() . "<br>";
function x() {
$path = './file.txt';
$content = file_get_contents($path);
foreach(explode("\n", $content) as $x) {
yield $x;
}
}
foreach(x() as $ex) {
$ex = trim($ex);
}
echo "Final memory usage: " . memory_get_usage();
The output for this script was:
Starting memory usage: 415152
Final memory usage: 415616
Clearly memory usage savings were considerable (ΔMemoryUsage -----> ~270.5 MB in first example, ~450B in second example).
With yield you can easily describe the breakpoints between multiple tasks in a single function. That's all, there is nothing special about it.
$closure = function ($injected1, $injected2, ...){
$returned = array();
//task1 on $injected1
$returned[] = $returned1;
//I need a breakpoint here!!!!!!!!!!!!!!!!!!!!!!!!!
//task2 on $injected2
$returned[] = $returned2;
//...
return $returned;
};
$returned = $closure($injected1, $injected2, ...);
If task1 and task2 are highly related, but you need a breakpoint between them to do something else:
free memory between processing database rows
run other tasks which provide dependency to the next task, but which are unrelated by understanding the current code
doing async calls and wait for the results
and so on ...
then generators are the best solution, because you don't have to split up your code into many closures or mix it with other code, or use callbacks, etc... You just use yield to add a breakpoint, and you can continue from that breakpoint if you are ready.
Add breakpoint without generators:
$closure1 = function ($injected1){
//task1 on $injected1
return $returned1;
};
$closure2 = function ($injected2){
//task2 on $injected2
return $returned1;
};
//...
$returned1 = $closure1($injected1);
//breakpoint between task1 and task2
$returned2 = $closure2($injected2);
//...
Add breakpoint with generators
$closure = function (){
$injected1 = yield;
//task1 on $injected1
$injected2 = (yield($returned1));
//task2 on $injected2
$injected3 = (yield($returned2));
//...
yield($returnedN);
};
$generator = $closure();
$returned1 = $generator->send($injected1);
//breakpoint between task1 and task2
$returned2 = $generator->send($injected2);
//...
$returnedN = $generator->send($injectedN);
note: It is easy to make mistake with generators, so always write unit tests before you implement them!
note2: Using generators in an infinite loop is like writing a closure which has infinite length...
An interesting aspect, which worth to be discussed here, is yielding by reference. Every time we need to change a parameter such that it is reflected outside of the function, we have to pass this parameter by reference. To apply this to generators, we simply prepend an ampersand & to the name of the generator and to the variable used in the iteration:
<?php
/**
* Yields by reference.
* #param int $from
*/
function &counter($from) {
while ($from > 0) {
yield $from;
}
}
foreach (counter(100) as &$value) {
$value--;
echo $value . '...';
}
// Output: 99...98...97...96...95...
The above example shows how changing the iterated values within the foreach loop changes the $from variable within the generator. This is because $from is yielded by reference due to the ampersand before the generator name. Because of that, the $value variable within the foreach loop is a reference to the $from variable within the generator function.
The below code illustrates how using a generator returns a result before completion, unlike the traditional non generator approach that returns a complete array after full iteration. With the generator below, the values are returned when ready, no need to wait for an array to be completely filled:
<?php
function sleepiterate($length) {
for ($i=0; $i < $length; $i++) {
sleep(2);
yield $i;
}
}
foreach (sleepiterate(5) as $i) {
echo $i, PHP_EOL;
}
When implement PHP IteratorAggregate interface, the yield keyword will be useful. Check out the documentation, there are couple examples either using ArrayIterator or yield.
Another example can be found is in the php-ds/polyfill repo: https://github.com/php-ds/polyfill/blob/e52796c50aac6e6cfa6a0e8182943027bacbe187/src/Traits/GenericSequence.php#L359
The idea is similar to the quick example below:
class Collection implements \IteratorAggregate
{
private $array = [];
public function push(...$values)
{
array_push($this->array, ...$values);
}
public function getIterator()
{
foreach ($this->array as $value) {
yield $value;
}
}
}
$collection = new Collection();
$collection->push('apple', 'orange', 'banana');
foreach ($collection as $key => $value) {
echo sprintf("[%s] => %s\n", $key, $value);
}
Output:
[0] => apple
[1] => orange
[2] => banana
This is PHP 5.4 code...
<?php
function abc($YesNo){return $YesNo["value"];}
$YesNo = array("value"=>"No","text"=>"No");
$x = array("active"=>function(){return abc($YesNo);});
echo $x['active']();
?>
Notice: Undefined variable: YesNo on line 7
Output Should be : Yes
if i directly put array in code by replace $YesNo like
<?php
function abc($YesNo){return $YesNo["value"];}
$x = array("active"=>function(){return abc(array("value"=>"Yes","text"=>"Yes"));});
echo $x['active']();
?>
output : Yes
which is correct output. Now what's the problem in first code. I need that for re-usability
Try this,
You can use use for passing data to a closure.
<?php
function abc($YesNo){return $YesNo["value"];}
$YesNo = array("value"=>"No","text"=>"No");
$x = array("active"=>function() use ($YesNo) {return abc($YesNo);});
echo $x['active']();
?>
You provide your anonymous function with a parameter:
$x = array("active"=>function($param){return abc($param);});
then you call it:
echo $x['active']($YesNo);
You may use the use keyword to make your function aware of an external variable:
$x = array("active"=>function() use ($YesNo) {return abc($YesNo);});
but it would be quite against the idea of reusability, in this case.
The problem is that your variable is not accessible within the function due to Variable Scope.
Because the array is defined outside of the function, it is not by default available inside the function.
There's a couple of solutions
Disclaimer: These are intended to fit within the scope of the question. I understand that they are not necessarily best practice, which would require a larger discussion
First Option:
You can declare the array within the function, like below. This is useful if you don't need access to it outside of the function.
function abc($YesNo){
$YesNo = array("value"=>"No","text"=>"No");
return $YesNo["value"];
}
Second Option:
Within your abc function, you can add the line global $YesNo. This is useful if you do need access to the array outside of the function:
function abc($YesNo){
global $YesNo;
return $YesNo["value"];
}
Other options exist (such as moonwave99's answer).
Finally:
Why are you putting an anonymous function within the array of $x? Seems like a path that will lead to problems down the road....
Your variable $YesNo needs to be visible in the scope of your anonymous function. You need to add global $YesNo as the first statement in that function:
So
$x = array("active"=>function(){return abc($YesNo);});
becomes
$x = array("active"=>function(){global $YesNo; return abc($YesNo);});
... also "value"=>"No" should be "value"=>"Yes" if you want it to return "Yes"
I am trying to create a PHP function that takes another PHP function as input. Here is an example:
function getMean(function){
$allUsers = getAllUsers();
$sum = 0;
foreach ($allUsers as $currentUser ){
$sum =+ (function($currentUser['CONSUMER_ID'], 5, 8))/(8-5);
}
}
Perhaps something like this should do it. PHP has a "type" called callback, which can be a closure (as of PHP 5.3), name of a function or array containing object and the method to call. These callbacks can be called with call_user_func()
function getMean($callback){
$allUsers = getAllUsers();
$sum = 0;
foreach ($allUsers as $currentUser ){
$sum =+ (call_user_func($callback, $currentUser['CONSUMER_ID'], 5, 8))/(8-5);
}
return $sum;
}
You need PHP 5.3 to do that natively.
function getMean($function){
$allUsers = getAllUsers();
$sum = 0;
foreach ($allUsers as $currentUser ){
$sum += ($function($currentUser['CONSUMER_ID'], 5, 8))/(8-5);
}
return $sum;
}
getMean(function($consumer_id, $five, $eight) {
return $consumer_id;
});
I you run PHP 5.3- (lower than 5.3), you need to use a callback (documentation is here) along with the call_user_func() or call_user_func_array() function.
you can do that as long as function($currentUser['CONSUMER_ID'], 5, 8) returns something
It looks like the PHP Eval function is what you are looking for. Pass the getMean function a string, and then change the line to have $sum =+ (eval($param) .....)
Security is a problem though because any function can be passed.
Save the function as a variable.
$function = function($param1){ //code here }
and pass it to your other function as a parameter.
function getMean($function)
{
//code here
$sum += $function($param);
}
[edit]
PHP.net Manual - Variable Functions
are you searching for anonymous functions?
Felix
If you want to dynamically create a function in php < 5.3 then youre stuck with using create_function. Becuase of how you use this function its really insane to use it for anything but but creating simple function for array_map,array_walk or things that do simple calculations or basic text processing.
If youre doing anything more complex than that its much easier and less of a headache to simply define the function as normal and then use it with call_user_func or call_user_func_array as others have suggested.
DO NOT USE eval :-)