I'm on a script which is going to make automatic appointment and the snippet below is for finding an available time. However, I've come across something that i've never heard of before.
When i use var_dump in my script to see the variables(i always do that when coding) the scripts run OK. However, when i remove these lines, script stops working as intended. as i cannot use var_dump for debugging since it makes the script running.
function getfirstfree($length, $limit) {
$length = new DateInterval('PT' . $length . 'M');
$table = "randevu";
$today = date('Y-m-d', time());
$q = $this->db->get_where('randevu', array('date' => $today));
$events = $q->result_array();
if ($q->num_rows() > 0) {
for ($i=0; $i < sizeof($q); $i += 1) {
$c_ends = new DateTime($events[$i]['ends']);
$n_starts = new DateTime($events[$i+1]['starts']);
$freetime = $c_ends->diff($n_starts);
$length_w_break = $length;
$length_w_break->i = $length_w_break->i + 10;
var_dump($length_w_break);
var_dump($freetime);
if ($freetime > $length_w_break) {
echo "ok";
return $c_ends->add(new DateInterval('PT10M'));
}
else {
return "no";
}
}
}
}
without var_dump the scripts returns no. with var_dump, it returns the first free slot.
current values taken from the database is
1-> starts 12:56:09 ends 12:56:09
2-> starts 13:33:11 ends 13:33:11
update var_dump($freetime); changes the output
In current PHP versions, DateInverval object and calculations with DateTime objects cause this bug. Avoid using them and use old-style, calculation with unix-time methods.
Related
I am new to php so please mind if it easy question. I have a php script, I want it to be executed only 10 times a day and not more than that. I don't want to use cron for this. Is there any way to do this in php only?
Right now I have set a counter which increases by one every time any one runs the script and loop it to 10 times only. if it exceeds it it shows an error message.
function limit_run_times(){
$counter = 1;
$file = 'counter.txt';
if(file_exists($file)){
$counter += file_get_contents($file);
}
file_put_contents($file,$counter);
if($counter > 11 ){
die("limit is exceeded!");
}
}
I want some efficient way to do this so everyday the script is only executed for 10 times and this is applicable for everyday i.e this counter gets refreshed to 0 everyday or is there any other efficient method.
I would rather recommend that you use a database instead - its cleaner and more simple to maintain.
However, it is achievable with file-handling as well. The file will be of format 2019-05-15 1 (separated by tab, \t). Fetch the contents of the file and split the values by explode(). Then do your comparisons and checks, and return values accordingly.
function limit_run_times() {
// Variable declarations
$fileName = 'my_log.txt';
$dailyLimit = 10;
$content = file_get_contents($fileName);
$parts = explode("\t", $content);
$date = $parts[0];
$counter = $parts[1] + 1;
// Check the counter - if its higher than 10 on this date, return false
if ($counter > $dailyLimit && date("Y-m-d") === $date) {
die("Daily executing limit ($dailyLimit) exceeded! Please try again tomorrow.");
}
// We only get here if the count is $dailyLimit or less
// Check if the date is today, if so increment the counter by 1
// Else set the new date and reset the counter to 1 (as it is executed now)
if (date("Y-m-d") !== $date) {
$counter = 1;
$date = date("Y-m-d");
}
file_put_contents($fileName, $date."\t".$counter);
return true;
}
I want to calculate the EMA (Exponential Moving Average) value in PHP.
I've tried with following code but it's giving me 500 error.
$real = array(12,15,17,19,21,25,28,12,15,16);
$timePeriod = 3;
$data = trader_ema($real,$timePeriod);
var_dump($data);
PHP: EMA calculation function trader-ema
Tried with long time Googling but not getting any help on this in PHP. So, I've no clue what needs to be done to calculate the EMA value.
Edit-1: Installed extensions
I've installed all the necessary extensions, Now I am getting the output.
But it doesn't seems giving proper output.
I think PHP function for calculating EMA is not working properly.
Any help in this would be greatly appreciated.
I recommend to use the math library from:
https://github.com/markrogoyski/math-php
public static function exponentialMovingAverage(array $numbers, int $n): array
{
$m = count($numbers);
$α = 2 / ($n + 1);
$EMA = [];
// Start off by seeding with the first data point
$EMA[] = $numbers[0];
// Each day after: EMAtoday = α⋅xtoday + (1-α)EMAyesterday
for ($i = 1; $i < $m; $i++) {
$EMA[] = ($α * $numbers[$i]) + ((1 - $α) * $EMA[$i - 1]);
}
return $EMA;
}
The trader extension for PHP actually looks quite promising. The underlying code looks very mature, and I notice at the time of writing the latest stable PHP module (0.5.1) was released at the first of this year with support for PHP8.
It may take some reading of the documentation, for example the note around trader_set_unstable_period, and god-forbid, the trader source code to become proficient.
If I do a quick installation of the trader module in a PHP Docker container
apt-get update
pecl install trader
docker-php-ext-enable trader
using the article from here as a benchmark
and put together a simple test script comparing the function supplied by #Tryke and trader_ema
function exponentialMovingAverage(array $numbers, int $n): array
{
$m = count($numbers);
$α = 2 / ($n + 1);
$EMA = [];
// Start off by seeding with the first data point
$EMA[] = $numbers[0];
// Each day after: EMAtoday = α⋅xtoday + (1-α)EMAyesterday
for ($i = 1; $i < $m; $i++) {
$EMA[] = ($α * $numbers[$i]) + ((1 - $α) * $EMA[$i - 1]);
}
return $EMA;
}
function merge_results($input, $avgs) {
$results = [];
$empty = count($input) - count($avgs);
foreach($input as $i => $price) {
$results[] = $i < $empty ? [$price, null] : [$price, round($avgs[$i], 2)];
}
return $results;
}
$real = [
22.27,
22.19,
22.08,
22.17,
22.18,
22.13,
22.23,
22.43,
22.24,
22.29,
22.15,
22.39,
22.38,
22.61,
23.36,
24.05,
23.75,
23.83,
23.95,
23.63,
23.82,
23.87,
23.65,
23.19,
23.10,
23.33,
22.68,
23.10,
22.40,
22.17
];
$timePeriod = 10;
$traderData = trader_ema($real,$timePeriod);
echo "trader ema\n";
var_dump(merge_results($real, $traderData));
$phpData = exponentialMovingAverage($real, 3);
echo "\n\nphp ema\n";
var_dump(merge_results($real, $phpData));
The results of the trader_ema match exactly. The results from Tryke's function do not. It seems to have results starting on the first day, whereas my expectation (and the output of the trader_ema and benchmark numbers reflect) is that there are no results until the $timePeriod has elapsed. See this note from the Investopedia article on EMA
Calculating the EMA requires one more observation than the SMA.
Suppose that you want to use 20 days as the number of observations for
the EMA. Then, you must wait until the 20th day to obtain the SMA. On
the 21st day, you can then use the SMA from the previous day as the
first EMA for yesterday.
I have such an array of intervals sorted by the lower bound ($a[$i] <= $a[$i+1] for every $i), key l is lower bound and , key h is upper bound and I'd like to remove all rows with intervals that are enclosed by larger intervals.
$a[0] = array('l' => 123, 'h'=>241);
$a[1] = array('l' => 250, 'h'=>360);
$a[2] = array('l' => 280, 'h'=>285);
$a[3] = array('l' => 310, 'h'=>310);
$a[4] = array('l' => 390, 'h'=>400);
So the result I'd like to get is
$a[0] = array('l' => 123, 'h'=>241);
$a[1] = array('l' => 250, 'h'=>360);
$a[2] = array('l' => 390, 'h'=>400);
This is what I attempted
function dup($a){
$c = count($a)-1;
for ($i = $c; $i > 0; $i --){
while ($a[$i]['h'] <= $a[$i-1]['h']){
unset($a[$i]);
}
}
$a = array_values($a);
}
The first answer which comes in mind was given with different variations by other contributors : for each interval, loop on each interval looking for a larger and enclosing interval. It's simple to understand and to write, and it works for sure.
This is basically n2 order, which means for n intervals we'll do n*n loop turns. There can be some tricks to optimize it :
break'ing when we find an enclosing interval in the nested loop, as in user3137702's answer, because it's useless to continue if we find at least one enclosing interval
avoiding looping on the same interval in the nested loop because we know an interval cant be strictly enclosed in itself (not significant)
avoiding looping on already excluded intervals in the nested loop (can have a significant impact)
looping on intervals (global loop) in ascending width = (h - l) order, because smaller intervals have more chance to be enclosed in others and the earliest we eliminate intervals, the more the next loop turns are effective (can be significant too in my opinion)
searching for enclosing intervals (nested loop) in descending width order, because larger intervals have more chance to be enclosing other intervals (I think it can have a significant impact too)
probably many other things that do not come to mind at the moment
Let me say now that :
optimization does not matter much if we have only few intervals to compute from time to time, and currently accepted user3137702's answer does the trick
to develop the suitable algorithm, it is necessary anyway to study the characteristics of the data that we have to deal with : in the case before us, how is the distribution of intervals ? Are there many enclosed intervals ? This can help to choose from the above list, the most useful tricks.
For educational purposes, I wondered if we could develop a different algorithm avoiding a n*n order which running time is necessarily very quickly deteriorated gradually as you increase the number of intervals to compute.
"Virtual rule" algorithm
I imagined this algorithm I called the "virtual rule".
place starting and ending points of the intervals on a virtual rule
run through the points along the rule in ascending order
during the run, register open or not intervals
when an interval starts and ends while another was opened before and is still open, we can say it is enclosed
so when an interval ends, check if it was opened after one of the other currently open intervals and if it is strictly closed before this interval. If yes, it is enclosed !
I do not pretend this is the best solution. But we can assume this is faster than the basic method because, despite many tests to do during the loop, this is n order.
Code example
I wrote comments to make it as clear as possible.
<?php
function removeEnclosedIntervals_VirtualRule($a, $debug = false)
{
$rule = array();
// place one point on a virtual rule for each low or up bound, refering to the interval's index in $a
// virtual rule has 2 levels because there can be more than one point for a value
foreach($a as $i => $interval)
{
$rule[$interval['l']][] = array('l', $i);
$rule[$interval['h']][] = array('h', $i);
}
// used in the foreach loop
$open = array();
$enclosed = array();
// loop through the points on the ordered virtual rule
ksort($rule);
foreach($rule as $points)
{
// Will register open intervals
// When an interval starts and ends while another was opened before and is still open, it is enclosed
// starts
foreach($points as $point)
if($point[0] == 'l')
$open[$point[1]] = $point[1]; // register it as open
// ends
foreach($points as $point)
{
if($point[0] == 'h')
{
unset($open[$point[1]]); // UNregister it as open
// was it opened after a still open interval ?
foreach($open as $i)
{
if($a[$i]['l'] < $a[$point[1]]['l'])
{
// it is enclosed.
// is it *strictly* enclosed ?
if($a[$i]['h'] > $a[$point[1]]['h'])
{
// so this interval is strictly enclosed
$enclosed[$point[1]] = $point[1];
if($debug)
echo debugPhrase(
$point[1], // $iEnclosed
$a[$point[1]]['l'], // $lEnclosed
$a[$point[1]]['h'], // $hEnclosed
$i, // $iLarger
$a[$i]['l'], // $lLarger
$a[$i]['h'] // $hLarger
);
break;
}
}
}
}
}
}
// obviously
foreach($enclosed as $i)
unset($a[$i]);
return $a;
}
?>
Benchmarking against basic method
It runs tests on randomly generated intervals
basic method works without a doubt. Comparing results from the two methods allows me to predent the "VirtualRule" method works because as far as I tested, it returned the same results
// * include removeEnclosingIntervals_VirtualRule function *
// arbitrary range for intervals start and end
// Note that it could be interesting to do benchmarking with different MIN and MAX values !
define('MIN', 0);
define('MAX', 500);
// Benchmarking params
define('TEST_MAX_NUMBER', 100000);
define('TEST_BY_STEPS_OF', 100);
// from http://php.net/manual/en/function.microtime.php
// used later for benchmarking purpose
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function debugPhrase($iEnclosed, $lEnclosed, $hEnclosed, $iLarger, $lLarger, $hLarger)
{
return '('.$iEnclosed.')['.$lEnclosed.' ; '.$hEnclosed.'] is strictly enclosed at least in ('.$iLarger.')['.$lLarger.' ; '.$hLarger.']'.PHP_EOL;
}
// 2 foreach loops solution (based on user3137702's *damn good* work ;) and currently accepted answer)
function removeEnclosedIntervals_Basic($a, $debug = false)
{
foreach ($a as $i => $valA)
{
$found = false;
foreach ($a as $j => $valB)
{
if (($valA['l'] > $valB['l']) && ($valA['h'] < $valB['h']))
{
$found = true;
if($debug)
echo debugPhrase(
$i, // $iEnclosed
$a[$i]['l'], // $lEnclosed
$a[$i]['h'], // $hEnclosed
$j, // $iLarger
$a[$j]['l'], // $lLarger
$a[$j]['h'] // $hLarger
);
break;
}
}
if (!$found)
{
$out[$i] = $valA;
}
}
return $out;
}
// runs a benchmark with $number intervals
function runTest($number)
{
// Generating a random set of intervals with values between MIN and MAX
$randomSet = array();
for($i=0; $i<$number; $i++)
// avoiding self-closing intervals
$randomSet[] = array(
'l' => ($l = mt_rand(MIN, MAX-2)),
'h' => mt_rand($l+1, MAX)
);
/* running the two methods and comparing results and execution time */
// Basic method
$start = microtime_float();
$Basic_result = removeEnclosedIntervals_Basic($randomSet);
$end = microtime_float();
$Basic_time = $end - $start;
// VirtualRule
$start = microtime_float();
$VirtualRule_result = removeEnclosedIntervals_VirtualRule($randomSet);
$end = microtime_float();
$VirtualRule_time = $end - $start;
// Basic method works for sure.
// If results are the same, comparing execution time. If not, sh*t happened !
if(md5(var_export($VirtualRule_result, true)) == md5(var_export($VirtualRule_result, true)))
echo $number.';'.$Basic_time.';'.$VirtualRule_time.PHP_EOL;
else
{
echo '/;/;/;Work harder, results are not the same ! Cant say anything !'.PHP_EOL;
stop;
}
}
// CSV header
echo 'Number of intervals;Basic method exec time (s);VirtualRule method exec time (s)'.PHP_EOL;
for($n=TEST_BY_STEPS_OF; $n<TEST_MAX_NUMBER; $n+=TEST_BY_STEPS_OF)
{
runTest($n);
flush();
}
Results (for me)
As I thought, clearly different performances are obtained.
I ran the tests on a Core i7 computer with PHP5 and on a (old) AMD Quad Core computer with PHP7. There are clear differences in performance between the two versions on my systems ! which in principle can be explained by the difference in PHP versions because the computer that is running PHP5 is much more powerful...
A simplistic approach, maybe not exactly what you want, but should at least point you in the right direction. I can refine it if needed, just a bit busy and didn't want to leave the question unanswered..
$out = [];
foreach ($a as $valA)
{
$found = false;
foreach ($a as $valB)
{
if (($valA['l'] > $valB['l']) && ($valA['h'] < $valB['h']))
{
$found = true;
break;
}
}
if (!$found)
{
$out[] = $valA;
}
}
This is entirely untested, but should end up with only the unique (large) ranges in $out. Overlaps as I mentioned in my comment are unhandled.
The problem was missing break in the while cycle
function dup($a){
$c = count($a)-1;
for ($i = $c; $i > 0; $i --){
while ($a[$i]['h'] <= $a[$i-1]['h']){
unset($a[$i]);
break; //here
}
}
$a = array_values($a);
}
Here is the code
function sort_by_low($item1,$item2){
if($item1['l'] == $item2['l'])
return 0;
return ($item1['l']>$item2['l'])? -1:1;
}
usort($a,'sort_by_low');
for($i=0; $i<count($a); $i++){
for($j=$i+1; $j<count($a);$j++){
if($a[$i][l]<=$a[$j]['l'] && $a[$i][h]>=$a[$j]['h']){
unset($a[$j]);
}
}
}
$a=array_values($a);
Here is the working code (Tested)
$result = array();
usort($a, function ($item1, $item2) {
if ($item1['l'] == $item2['l']) return 0;
return $item1['l'] < $item2['l'] ? -1 : 1;
});
foreach ($a as $element) {
$exists = false;
foreach ($result as $r) {
if (($r['l'] < $element['l'] && $r['h'] > $element['h'])) {
$exists = true;
break;
}
}
if (!$exists) {
$result[] = $element;
}
}
$result will contain the desired result
<?php
// initial value
$week = 50;
$year = 2001;
$store = array();
do
{
$week++;
$result = "$week/$year";
array_push($store,$result);
if($week == 53){
$week = 0;
$year++;//increment year by 1
}
continue;
}
// End of Loop
while ($result !== "2/2002");
?>
print_r($store);
result want return will be
array("51/2001", "52/2001", "01/2002", "02/2002");
What is my problems by using while using do..while ,continue?
Your arguments to array_push are the wrong way around. Read the manual entry for functions you use. Turn on warnings on your server; running this in codepad showed me the problem immediately. [Edit: You have now quietly fixed that in your question.]
You also have a typo: $i instead of $week.
Finally, you test against "02/2002", but for that month the string will be "2/2002".
Fixed code (live demo):
<?php
// initial value
$week = 50;
$year = 2001;
$store = array();
do
{
$week++;
$result = "$week/$year";
array_push($store, $result);
if($week == 53){
$week = 0;
$year++;//increment year by 1
}
continue;
}
// End of Loop
while ($result !== "2/2002");
?>
In general, I'd recommend against loops like this. As you've discovered, your code is very fragile because you're testing for just one very specific value, and if that value is not precisely correct you get an infinite loop.
Instead, consider comparing $week and $year separately and numerically:
while ($week < 2 && $year <= 2002)
Next time please include in your question the output that you are seeing, as well as the output that you want to see. It'll save us time in reproducing your problem.
I may not be understanding this correctly... If you could explain a bit more that'd help.
Try turning the loop into a function, and turn the while(..) to check the functions variable.
then just call it 4 times to fill your array.
Hey there. Today I wrote a small benchmark script to compare performance of copying variables vs. creating references to them. I was expecting, that creating references to large arrays for example would be significantly slower than copying the whole array. Here is my benchmark code:
<?php
$array = array();
for($i=0; $i<100000; $i++) {
$array[] = mt_rand();
}
function recursiveCopy($array, $count) {
if($count === 1000)
return;
$foo = $array;
recursiveCopy($array, $count+1);
}
function recursiveReference($array, $count) {
if($count === 1000)
return;
$foo = &$array;
recursiveReference($array, $count+1);
}
$time = microtime(1);
recursiveCopy($array, 0);
$copyTime = (microtime(1) - $time);
echo "Took " . $copyTime . "s \n";
$time = microtime(1);
recursiveReference($array, 0);
$referenceTime = (microtime(1) - $time);
echo "Took " . $referenceTime . "s \n";
echo "Reference / Copy: " . ($referenceTime / $copyTime);
The actual result I got was, that recursiveReference took about 20 times (!) as long as recursiveCopy.
Can somebody explain this PHP behaviour?
PHP will very likely implement copy-on-write for its arrays, meaning when you "copy" an array, PHP doesn't do all the work of physically copying the memory until you modify one of the copies and your variables can no longer reference the same internal representation.
Your benchmarking is therefore fundamentally flawed, as your recursiveCopy function doesn't actually copy the object; if it did, you would run out of memory very quickly.
Try this: By assigning to an element of the array you force PHP to actually make a copy. You'll find you run out of memory pretty quickly as none of the copies go out of scope (and aren't garbage collected) until the recursive function reaches its maximum depth.
function recursiveCopy($array, $count) {
if($count === 1000)
return;
$foo = $array;
$foo[9492] = 3; // Force PHP to copy the array
recursiveCopy($array, $count+1);
}
in recursiveReference you're calling recursiveCopy... this doesn't make any sense, in this case you're calling recursiveReference just once. correct your code, rund the benchmark again and come back with your new results.
in addition, i don't think it's useful for a benchmark to do this recursively. a better solution would be to call a function 1000 times in a loop - once with the array directly and one with a reference to that array.
You don't need to (and thus shouldn't) assign or pass variables by reference just for performance reasons. PHP does such optimizations automatically.
The test you ran is flawed because of these automatic optimizations. In ran the following test instead:
<?php
for($i=0; $i<100000; $i++) {
$array[] = mt_rand();
}
$time = microtime(1);
for($i=0; $i<1000; $i++) {
$copy = $array;
unset($copy);
}
$duration = microtime(1) - $time;
echo "Normal Assignment and don't write: $duration<br />\n";
$time = microtime(1);
for($i=0; $i<1000; $i++) {
$copy =& $array;
unset($copy);
}
$duration = microtime(1) - $time;
echo "Assignment by Reference and don't write: $duration<br />\n";
$time = microtime(1);
for($i=0; $i<1000; $i++) {
$copy = $array;
$copy[0] = 0;
unset($copy);
}
$duration = microtime(1) - $time;
echo "Normal Assignment and write: $duration<br />\n";
$time = microtime(1);
for($i=0; $i<1000; $i++) {
$copy =& $array;
$copy[0] = 0;
unset($copy);
}
$duration = microtime(1) - $time;
echo "Assignment by Reference and write: $duration<br />\n";
?>
This was the output:
//Normal Assignment without write: 0.00023698806762695
//Assignment by Reference without write: 0.00023508071899414
//Normal Assignment with write: 21.302103042603
//Assignment by Reference with write: 0.00030708312988281
As you can see there is no significant performance difference in assigning by reference until you actually write to the copy, i.e. when there is also a functional difference.
Generally speaking in PHP, calling by reference is not something you'd do for performance reasons; it's something you'd do for functional reasons - ie because you actually want the referenced variable to be updated.
If you don't have a functional reason for calling by reference then you should stick with regular parameter passing, because PHP handles things perfectly efficiently that way.
(that said, as others have pointed out, your example code isn't exactly doing what you think it is anyway ;))
In recursiveReference() function you call recursiveCopy() function. It it what you really intended to do?
You do nothing with $foo variable - probably it was supposed to be used in further method call?
Passing variable by reference should generally save stack memory in case of passing large objects.
recursiveReference is calling recursiveCopy.
Not that that would necessarily harm performance, but that's probably not what you're trying to do.
Not sure why performance is slower, but it doesn't reflect the measurement you're trying to make.