I know what ticks are in PHP, but looking at the output of the following code:
<?php
function myfunc() {
static $n = 1;
print "$n) Tick<br>";
$n++;
}
register_tick_function("myfunc");
declare(ticks=1);
echo 'echo<br>';
The output is:
1) Tick
2) Tick
echo
3) Tick
It tells me that the registered tick function 'myfunc' is executed 3 times. But, based on this answer -> PHP using Declare ? What is a tick?:
You get a tick for each line ; and each block {}
Shouldn't it be:
1) Tick
echo
2) Tick
? As there are only two statements:
declare(ticks=1);<-- Statement 1
echo 'echo<br>';<-- Statement 2
Why 3??? If I remove the ";" from declare, like this:
declare(ticks=1)
echo 'echo<br>';
I get the only one execution of the registered tick function:
echo
1) Tick
So what is the definitely rule to count the tickable statements in order to understand how many times a registered tick function is executed? (I am asking it because of this example and because PHP manual actually doesn't cover the topic on counting tickable stats)
EDIT: Another strange behaviour in my opinion is this:
<?php
function myfunc()
{
static $n = 1;
print "$n) Tick<br>";
$n++;
}
register_tick_function("myfunc");
declare(ticks = 1)
echo 'Start<br>';
echo 'echo<br>';
which outputs:
Start
1) Tick
echo
The tick function is executed once, but the statements are at least 2 (if not counting the "end of the script" as #Marc B has pointed out)
what I finelly found is:
function myfunc()
{
static $n = 1;
print "$n) Tick<br>";
$n++;
}
register_tick_function("myfunc");
declare(ticks = 1) {
//echo 'Start<br>';
echo 'echo<br>';
}
outputs 2 ticks, one for block {} and 1 for echo.
if you uncomment 'Start'
that will bring 1 more tick as you expected.
So I think the best practice is to always use
declare(ticks=1) { }
with block brackets
You don't put a semicolon after the declare, so your declare statement works only for the next statement (for one only echo). It is the same behaviour, as with using a block in curly brackets after declare - that block is then regarded as the only statement to execute. You have the same with control structures: while(true)x(); and while(true){x();y();}, just in the case with declare semicolon after it creates an implicit block around all the remaining script.
Related
I have some code that needs a list of functions.
I want to create automatically part of this list.
Scope rules in PHP prevent my following approach to the problem from working:
$list=[];
$i=0;
for(;$i<10; $i++) {
$list []= function() {
// code that --depends-- on $i, like:
return $i;
};
}
// example of use of the 7th function in the list:
echo $list[6]();
Outputs (on my test machine I have PHP 8.1.2)
Warning: Undefined variable $i in [...]/test.php on line [...]
Because when the 7th anonymous function in $list is called by the last line, its body refers to $i, which is local to the function body and does not refer to the loop variable $i. If $i were declared global it would not issue a warning but would not do what I want either.
Notes:
In some posts here, OO programming is mentioned. But I do not know enough about OO programming and PHP to see how.
create_function is no more available in PHP 8
I know there are simpler code for outputting 6, like echo 6; thanks.
<?php
$list=[];
for($i=0;$i<10; $i++) {
$list []= function() use ($i) { // <--
return $i;
};
}
echo $list[6]();
You are missing the capture phrase in PHP: https://www.php.net/manual/en/functions.anonymous.php
Closures may also inherit variables from the parent scope. Any such variables must be passed to the use language construct.
I am learning about static variables in PHP and came across this code in PHP manual.
<?php
function test() {
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
?>
I couldn't understand the purpose of last $count--;. So, I wrote a different version of the function below:
<?php
function test() {
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
echo 'I am here!';
$count--;
}
test();
?>
The output of above code is:
12345678910I am here!I am here!I am here!I am here!I am here!I am here!I am here!I am here!I am here!I am here!
Why isn't the output just the line below because we go past the if condition only once.
12345678910I am here!
If we are going past the if condition multiple times, then shouldn't the output be:
1I am here!2I am here!3I am here!4I am here!5I am here!6I am here!7I am here!8I am here!9I am here!10I am here!
Thanks.
This is more about recursion than static variables. However:
Why the numbers are written out first and the text afterwards? Let's break each run of the function. For simplification, I'll only use example with 2 calls (if ($count < 2))
1st call starts, $count is incremented to 1
prints 1
Within the 1st call, the condition $count < 2 is met, so it calls test() (so that's going to be the 2nd call)
2nd call starts, $count is incremented to 2 (if it weren't static, it wouldn't keep the value from the higher scope)
prints 2
Within the 2nd call, the condition $count < 2 is NOT met, so it skips the if block
prints I am here! and ends the 2nd call
Now the 1st call is done running the recursive function so it continues
prints I am here! and ends the 1st call
When you're calling test() within the method that doesn't stop the execution of the rest of the code in the method.
The reason, as far as i can see, it doesn't output a number after each string of "i am here" is because you're calling the method test() before the output. So each time it's waiting for that method to complete before moving on to the next string.
If you were to move the $count echo to after it I believe it'd output as expected.
Does that answer your question at all?
I need a variable to be passed along several functions & if statements, i'm going to keep it short.
I start off with initializing a static counter which i will use to keep track of the case number in my mysql database;
static $counter = 1;
then i write my function in which i try to simply increment my global variable (this is in an if statement inside my function);
$counter++;
Now my code compiles and runs perfectly but the counter seems to never increment and give every case id 1.
Anyone know how i managed to mess this up?
EDIT (Current structure):
<?php
static $counter = 1;
function frontend($connection){
global $counter;
(...)
if(isset($_POST['submit'])){
(...)
if(isset($_POST['betaald'])){
$counter++;
}
}
} ...
Now this code makes a neat database of all i need except the counter which seems to be unchangeable.
Explain more about your code and see the example.
<?php
function keep_track() {
STATIC $count = 0;
$count++;
print $count;
print "<br />";
}
keep_track();
keep_track();
keep_track();
?>
This will produce the following result −
1
2
3
The background of this story is pretty long, so to keep things short -- I know goto is bad, but I have no other choice, because... PHP lacks comma operator.
I have such pattern -- regular function with entry point given as label, and inside it a small lambda which needs to goto to that entry point. Something like this (incorrect code):
function water()
{
_Entry_point_2_0:
// ... some code
(function() { /*...*/ ;goto _Entry_point_2_0;})();
// ... some code
}
I cannot just jump across the function boundaries, so my next idea is to return the label from the lambda and use it as "value" for goto. Something like this:
function water()
{
_Entry_point_2_0:
// ... some code
goto (function() { /*...*/ ;return '_Entry_point_2_0';})();
// ... some code
}
This does not work. Evaluating entire goto as a string eval ('goto _Entry_point_2_0;') does not work either.
The crazy part is I know the label in advance, so you can ask why I cannot write entire function like that:
function water()
{
_Entry_point_2_0:
// ... some code
(function() { /*...*/ ;})();
goto _Entry_point_2_0;
// ... some code
}
The problem is in logic -- executing lambda and goto make 2 expressions now, not one. And I need to make it in one expression -- execute lambda and goto has to be packed in single expression.
I also cannot call recursively the main function because it is whole point of this work, to avoid recursive call :-).
What are the other ways to achieve this?
UPDATE 1 Maybe I rephrase -- what I would like to achieve with goto is continue my_function. Executed from or at the boundary of the inner function (i.e. lambda).
UPDATE 2 The main goal is to loop over the main function, so it is almost equivalent to:
function water()
{
_Entry_point_2_0: while (true)
{
// ... some code
continue (function() { /*...*/ ; return '_Entry_point_2_0'; })();
// ... some code
}
}
"Almost" because of two reasons. I still have the problem with labels exactly as before, and what's more now I have problem where to add breaks to the loop.
So you can't skip a loop iteration from within an anonymous/lambda function. But you could return a value, compare values in the main function and then skip the iteration from there. Sounds simple, right?
Edit: if you also want to break from the lamba, you can use the same strategy.
function water() {
$lambda = function() { /*...*/; };
while (true) {
// Call the lambda function and store the return value.
$return = $lambda();
// If the return value was 'skip this iteration', well... skip it.
// Note: I'd normally compare to false, null or similar.
if ($return == 'skip this iteration') {
continue;
}
elseif ($return == "we're done!") {
break;
}
// Do stuff here.
}
}
I need to make it in one expression -- execute lambda and goto has to be packed in single expression.
So from your last comment I now partly understand why you have this requirement. But it's still not entirely clear. You have written a script to inject this code into some pre-existing function(s) and don't know the surroundings. But are the goto labels already in place? In any case, perhaps your problem is as simple as this: you want the code to be a single line only. But that's possible, code can span only a single line in PHP. It's not the prettiest, but it works just fine.
function water() {
while (true) {
if (function() { /*...*/; }() == 'skip this iteration') { continue; /* Or goto */ }
// Do stuff here.
}
}
Perhaps you are trying to solving your attemted solution rather than your original problem. This question smells XY problem.
_Entry_point_2_0:
// ... some code
(function() { /*...*/ ;goto _Entry_point_2_0;})();
Looks for me like do while loop:
do {
// ... some code
} while ((function() { /*...*/ ; return $k !== 0;})());
Now applying an anonymous function like that is not allowed. In addition closure parameters need to be explicitly declared. Thus my solution needs to be written like this:
$k = 10;
$f = function() use (&$k) { /*...*/ ; return $k !== 0; };
do {
// some code
} while ( $f() );
If you want to have a "comma operator" you just make a function that takes any numbers of argumens and return the last:
function begin(){
return func_get_args()[func_num_args()-1];
}
begin(expression1, expression2, expression3); // ==> result of expression3
All of a function arguments gets evaluated so it does the same given that the arguments are not dependent on each other.
Im fairly sure this is impossible by design.
Annonymous functions require all "state" to be passed in via a use statement. You can not pass a label as a reference
PHP Parse error: syntax error, unexpected '_Entry_point_2_0'
(T_STRING), expecting '&' or variable (T_VARIABLE)
The function is running in its own scope with no reference to the label which means it would need to be passed back somehow.
goto docs have the following
This is not a full unrestricted goto. The target label must be within the same file and context, meaning that you cannot jump out of a function or method, nor can you jump into one.
its also invalid to pass a variable to goto
PHP Parse error: syntax error, unexpected '$goto' (T_VARIABLE), expecting identifier (T_STRING)
Though all of this is probably a good thing, as goto suggests the logic flow is wrong, You will be able to find a way to structure this that is more corect and does not require a go to.
Edit:
After update 2 why not do
if(your lambda() == something) {
Continue;
}
Sorry wrote the code on my phone. That seems to do the same thing and is much more readable
You can use recursion:
function myFunc($i = 0) {
if ($i < 10) { //or any other logic to prevent "infinite loop"
//Main code
myFunc($i++);
}
}
I am new to PHP. Well the text i am referring to say quotes
Functions can be defined anywhere within your program.
the above statement holds fine for code block 1 but not for code block 2. KINDLY EXPLAIN?
CODE Block 1:
<?php
test();
function test()
{
echo "Hello Inside the function";
}
?>
CODE Block 2:
<?php
$no=1;
switch ($no)
{
case "1":
test();
function test()
{
echo "Hello test";
}
}
?>
In theory, yes, functions can be defined "anywhere". In practice, there's a trick to it. The trick is as follows: when PHP reads and compiles the source of your script, it looks for function definitions, and if function definition is in global context (not inside if, switch, etc.) it will be defined immediately. However, if it is inside such construct, or inside another function, etc. it will be defined only when control passes the line on which function() statement resides.
Thus, code block 1 works - because the function is in global context, so PHP will define it before any code is run. But in code block 2, the function is in the context of switch, so it will be defined only when control passes line 7. But since you try to call it on line 6, it is not defined yet! So PHP errors out.
The advice here is never define your functions inside conditionals etc. unless you mean it to be conditional definitions - and then take care not to call them before they are defined.
You can declare function in switch statement, but it's not so good. Your have error because you call function and just then declare it. At first you should declare function and then use it.
<?php
$no=1;
switch ($no)
{
case "1":
function test()
{
echo "Hello test";
}
test();
}
?>
You cannot declare a function in a switch statement.
However what you can do is the following:
<?php
$no=1;
switch ($no)
{
case "1":
test();
break;
}
function test()
{
echo "Hello test";
}
?>
Just remove the function from the switch.
The function only gets executed when called so it doesn't matter.
EDIT
What propably is meant by that quote (Functions can be defined anywhere within your program.) is:
You can declare functions before or even after you call them in your script.
A couple of problems. you need to use
case 1:
for switches, otherwise it will be looking for a string equivalent to "1". "1" != 1 (the first is a string, the second an integer)
While your text did say functions could be defined anywhere, they didn't actually mean anywhere. You cannot define a function inside of a block of code, so you'l have to define the function outside of the switch:
<?php
$no = 1;
switch ($no) {
case 1:
test();
break;
}
function test()
{
echo "I'm inside the test function!";
}
?>
Otherwise things just get crazy.