This question already has answers here:
Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
(3 answers)
Closed 4 years ago.
I'd like to report how many files get deleted from a function that I'm running within php via a cron task.
Current codes is as follows:-
<?php
function deleteAll($dir) {
$counter = 0;
foreach(glob($dir . '/*') as $file) {
if(is_dir($file)) {
deleteAll($file); }
else {
if(is_file($file)){
// check if file older than 14 days
if((time() - filemtime($file)) > (60 * 60 * 24 * 14)) {
$counter = $counter + 1;
unlink($file);
}
}
}
}
}
deleteAll("directory_name");
// Write to log file to confirm completed
$fp = fopen("logthis.txt", "a");
fwrite($fp, $counter." files deleted."."\n");
fclose($fp);
?>
That makes sense to me with a VBA background, but the counter returns null I think when written to my custom log file at the end. I presume there is some restriction on a shared hosting site of being able to declare the variable globally or similar?
Appreciate any help! It's not the end of world if I can't count the deleted files, but it would be nice to log the output in the format I've chosen.
This doesnt work due to scopes. In your example $counter only exists inside your function.
function deleteAll($dir):int {
$counter = 0; // start with zero
/* Some code here */
if(is_dir($file)) {
$counter += deleteAll($file); // also increase with the recursive amount
}
/* Some more code here */
return $counter; // return the counter (at the end of the function
}
$filesRemoved = deleteAll("directory_name");
Alternatively, if you want to send back more info, eg 'totalCheck' etc, you can send back an array of info:
function deleteAll($dir):array {
// All code here
return [
'counter' => $counter,
'totalFiles' => $allFilesCount
];
}
$removalStats = deleteAll("directory_name");
echo $removalStats['counter'].'files removed, total: '.$removalStats['totalFiles'];
There are other solutions like 'pass-by-reference', but you dont want those.
Related
Problem
I have tried to write an only list-urls sitemap on a txt file. A file is generated daily and can be updated.
Trial
generateSitemap is part of a large class UpdateStocks which gets an input string and writes a URL for that input iterating about ~8-10K. Inputs are being generated using data from an API right before going to generateSitemap.
Performance
Would you be so kind and help me to possibly make it faster, simpler or more efficient? There is a small bug in generateSitemap that I could not find out, when it updates the file, sometimes, there is an extra newline \n in the txt file.
Pseudocode that calls the generateSitemap
{pseudocode} for i=1 to 8000;
generate input[i]; // for example: 'aapl-apple-technology-nasdaq-us-8f4c'
UpdateStocks::generateSitemap(input[i]);
{/pseudocode} endfor;
Class Constant
const DIR_URL_KEYWORD_1 = "equity";
const DIR_URL_KEYWORD_2 = "equilibrium-estimation";
const DOMAIN = "domain.org";
const EXTENSION_MD = ".md";
const EXTENSION_TXT = ".txt";
const NEW_LINE = "\n";
const PROTOCOL = "https://";
const SITEMAP_PREFIX = "/sitemap-";
const SLASH = "/";
generateSitemap
/**
*
* #return a large string in a txt file including all urls for a daily sitemap
*/
public static function generateSitemap($lurl){
$dir=__DIR__ . self::DIR_FRONT_PUBLIC_HTML;
// url
$sm=sprintf('%s%s%s',
self::PROTOCOL.self::DOMAIN.self::SLASH.self::DIR_URL_KEYWORD_1.self::SLASH.self::DIR_URL_KEYWORD_2.self::SLASH,
$lurl,
self::NEW_LINE
);
$dt=new \DateTime('now');
$dt=$dt->format('Y-m-d'); // today
$fn=$dir . self::SITEMAP_PREFIX . $dt . self::EXTENSION_TXT; // sitemap filename in public_html
// if daily sitemap already exits
if(file_exists($fn)){
$arr = preg_split('/\n/', trim(file_get_contents($fn))); // array of links
$i=0; // counter
foreach ($arr as $k=>$lk){
if($arr[$k]==null){unset($arr[$k]);}
if(trim($lk)===trim($sm)){ // link already exist
$i++;
if($i>0){$arr[$k]=null;} // link already exist more than once
}else{
if($k==sizeof($arr)-1){
$k++;
$arr[$k]=$sm;
$arr=implode(self::NEW_LINE, $arr);
$fh=fopen($fn, 'wb');
fwrite($fh, $arr);
fclose($fh);
}
continue;
}
}
}else{
$fh=fopen($fn, 'wb');
fwrite($fh, $sm);
fclose($fh);
}
}
Example of Inputs
a-agilent-technologies-healthcare-nyse-us-39d4
aa-alcoa-basic-materials-nyse-us-159a
aaau-perth-mint-physical-gold-nyse-us-8ed9
aaba-altaba-financial-services-nasdaq-us-26f5
aac-healthcare-nyse-us-e92a
aadr-advisorshares-dorsey-wright-adr-nyse-us-d842
aal-airlines-industrials-nasdaq-us-29eb
aamc-altisource-asset-management-com-financial-services-nyse-us-b46a
aan-aarons-industrials-nyse-us-d00e
aaoi-applied-optoelectronics-technology-nasdaq-us-1dee
aaon-basic-materials-nasdaq-us-238e
aap-advance-auto-parts-wi-consumer-cyclical-nyse-us-1f60
aapl-apple-technology-nasdaq-us-8f4c
aat-assets-real-estate-nyse-us-3598
aau-almaden-minerals-basic-materials-nyse-us-1c57
aaww-atlas-air-worldwide-industrials-nasdaq-us-69f3
aaxj-ishares-msci-all-country-asia-ex-japan-nasdaq-us-c6c4
aaxn-axon-enterprise-industrials-nasdaq-us-0eef
ab-alliancebernstein-units-financial-services-nyse-us-deb1
abac-renmin-tianli-consumer-defensive-nasdaq-us-8701
abb-industrials-nyse-us-a407
abbv-abbvie-healthcare-nyse-us-9aea
abc-amerisourcebergen-healthcare-nyse-us-bd9d
abcb-ameris-bancorp-financial-services-nasdaq-us-df98
abdc-alcentra-capital-financial-services-nasdaq-us-96dd
abeo-abeona-therapeutics-healthcare-nasdaq-us-aa0f
abeow-market-us-d84d
abev-ambev-1-consumer-defensive-nyse-us-a9b4
abg-asbury-automotive-consumer-cyclical-nyse-us-db5f
abil-ability-technology-nasdaq-us-91a6
abio-arca-biopharma-healthcare-nasdaq-us-098e
abm-abm-industries-industrials-nyse-us-bcbc
abmd-abiomed-healthcare-nasdaq-us-2818
abr-arbor-realty-real-estate-nyse-us-68b1
abr-a-arbor-realty-real-estate-nyse-us-8c1d
abr-b-arbor-realty-real-estate-nyse-us-97f2
abr-c-arbor-realty-real-estate-nyse-us-ee81
abt-abbott-laboratories-healthcare-nyse-us-c7fd
abtx-allegiance-bancshares-financial-services-nasdaq-us-6913
abus-arbutus-biopharma-healthcare-nasdaq-us-c23f
ac-associated-capital-financial-services-nyse-us-fca3
aca-arcosa-industrials-nyse-us-b429
Part of sitemap-2019-03-15.txt:
domain.org/equity/equilibrium-estimation/a-agilent-technologies-healthcare-nyse-us-39d4
domain.org/equity/equilibrium-estimation/aa-alcoa-basic-materials-nyse-us-159a
domain.org/equity/equilibrium-estimation/aaau-perth-mint-physical-gold-nyse-us-8ed9
domain.org/equity/equilibrium-estimation/aaba-altaba-financial-services-nasdaq-us-26f5
domain.org/equity/equilibrium-estimation/aac-healthcare-nyse-us-e92a
domain.org/equity/equilibrium-estimation/aadr-advisorshares-dorsey-wright-adr-nyse-us-d842
domain.org/equity/equilibrium-estimation/aal-airlines-industrials-nasdaq-us-29eb
domain.org/equity/equilibrium-estimation/aamc-altisource-asset-management-com-financial-services-nyse-us-b46a
domain.org/equity/equilibrium-estimation/aan-aarons-industrials-nyse-us-d00e
domain.org/equity/equilibrium-estimation/aaoi-applied-optoelectronics-technology-nasdaq-us-1dee
domain.org/equity/equilibrium-estimation/aaon-basic-materials-nasdaq-us-238e
domain.org/equity/equilibrium-estimation/aap-advance-auto-parts-wi-consumer-cyclical-nyse-us-1f60
domain.org/equity/equilibrium-estimation/aapl-apple-technology-nasdaq-us-8f4c
domain.org/equity/equilibrium-estimation/aat-assets-real-estate-nyse-us-3598
domain.org/equity/equilibrium-estimation/aau-almaden-minerals-basic-materials-nyse-us-1c57
domain.org/equity/equilibrium-estimation/aaww-atlas-air-worldwide-industrials-nasdaq-us-69f3
domain.org/equity/equilibrium-estimation/aaxj-ishares-msci-all-country-asia-ex-japan-nasdaq-us-c6c4
domain.org/equity/equilibrium-estimation/aaxn-axon-enterprise-industrials-nasdaq-us-0eef
domain.org/equity/equilibrium-estimation/ab-alliancebernstein-units-financial-services-nyse-us-deb1
domain.org/equity/equilibrium-estimation/abac-renmin-tianli-consumer-defensive-nasdaq-us-8701
domain.org/equity/equilibrium-estimation/abb-industrials-nyse-us-a407
domain.org/equity/equilibrium-estimation/abbv-abbvie-healthcare-nyse-us-9aea
domain.org/equity/equilibrium-estimation/abc-amerisourcebergen-healthcare-nyse-us-bd9d
domain.org/equity/equilibrium-estimation/abcb-ameris-bancorp-financial-services-nasdaq-us-df98
domain.org/equity/equilibrium-estimation/abdc-alcentra-capital-financial-services-nasdaq-us-96dd
domain.org/equity/equilibrium-estimation/abeo-abeona-therapeutics-healthcare-nasdaq-us-aa0f
domain.org/equity/equilibrium-estimation/abeow-market-us-d84d
domain.org/equity/equilibrium-estimation/abev-ambev-1-consumer-defensive-nyse-us-a9b4
domain.org/equity/equilibrium-estimation/abg-asbury-automotive-consumer-cyclical-nyse-us-db5f
domain.org/equity/equilibrium-estimation/abil-ability-technology-nasdaq-us-91a6
domain.org/equity/equilibrium-estimation/abio-arca-biopharma-healthcare-nasdaq-us-098e
domain.org/equity/equilibrium-estimation/abm-abm-industries-industrials-nyse-us-bcbc
domain.org/equity/equilibrium-estimation/abmd-abiomed-healthcare-nasdaq-us-2818
domain.org/equity/equilibrium-estimation/abr-arbor-realty-real-estate-nyse-us-68b1
domain.org/equity/equilibrium-estimation/abr-a-arbor-realty-real-estate-nyse-us-8c1d
domain.org/equity/equilibrium-estimation/abr-b-arbor-realty-real-estate-nyse-us-97f2
domain.org/equity/equilibrium-estimation/abr-c-arbor-realty-real-estate-nyse-us-ee81
domain.org/equity/equilibrium-estimation/abt-abbott-laboratories-healthcare-nyse-us-c7fd
domain.org/equity/equilibrium-estimation/abtx-allegiance-bancshares-financial-services-nasdaq-us-6913
domain.org/equity/equilibrium-estimation/abus-arbutus-biopharma-healthcare-nasdaq-us-c23f
domain.org/equity/equilibrium-estimation/ac-associated-capital-financial-services-nyse-us-fca3
domain.org/equity/equilibrium-estimation/aca-arcosa-industrials-nyse-us-b429
Here is an untested script that embodies how I would run it (unless we are dealing with prohibitively large file sizes).
Collect and prepare all of api strings into a single array.
If the first data of the day, just push the data into a new file.
If the file exists, extract the old data, merge with the new, purge the duplicates, alphabetize, then replace the file contents.
public static function collectAPIData() {
$leading_url = self::PROTOCOL .
self::DOMAIN .
self::SLASH .
self::DIR_URL_KEYWORD_1 .
self::SLASH .
self::DIR_URL_KEYWORD_2 .
self::SLASH;
$fresh_data = [];
// start loop
$fresh_data[] = $leading_url . $your_string_from_the_api;
// end loop
return $fresh_data;
}
public static function storeSitemapData($new_urls) {
if (!$new_urls)) {
return;
}
$fn = __DIR__ .
self::DIR_FRONT_PUBLIC_HTML .
self::SITEMAP_PREFIX .
(new \DateTime('now'))->format('Y-m-d') .
self::EXTENSION_TXT;
if (file_exists($fn)) {
$old_urls = file($fn, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$merged = array_merge($old_urls, $new_urls);
$unique = array_keys(array_flip($merged));
sort($unique);
$new_urls = $unique;
}
file_put_contents($fn, implode(self::NEW_LINE, $new_urls));
}
These static functions could be called something like this:
UpdateStocks::storeSitemapData(UpdateStocks::collectAPIData));
In truth, for higher efficiency I could have differentiated new unique urls, then appended them to the existing file, but I like the idea of keeping the data alphabetized.
$i=0; // counter
foreach ($arr as $k=>$lk){
if($arr[$k]==null)
{unset($arr[$k]);}
if(trim($lk)===trim($sm))
{
if($i>0){$arr[$k]=null;}
$i++;
}
$i++ should come after if statement
I am working on a project that requires me to check for the existence of a file with PHP every 5 seconds, and if the file exists, redirect to it.
If it doesn't exist, the script should keep checking every 5 seconds instead.
How would I go about doing that? I know about file_exists(), but how would I go about making it check continuously, not just once?
you can try using this
<?php
$x = 0;
$count = 5;
do {
if(!file_exists($file)){
$x++;
echo 'file loading';
sleep(5);//Delays the program execution for 5seconds before code continues.
}
else {
header('Location: '.$file);
exit();
}
}
while($x < $count); // this kind of regulates how long the loop should last to avoid maximum execution timeout error
if($x == $count){
echo 'file does not exist';
}
?>
A while ago I made a post (Searching for a specific string from all PHP files in the parent directory) that was about me finding the position of the file path in an array, only if the file had a specific keyword.
However, that was in my old website, which I have now lost. So I am currently recreating it. However, for some reason this function does not work.
public function build_active_theme() {
$dir = CONPATH . '/themes/' . $this->get_active_theme() . '/';
$theme_files = array();
foreach(glob($dir . '*.php') as $file) {
$theme_files[] = $file;
}
$count = null;
foreach($theme_files as $file) {
$file_contents = file_get_contents($file);
if(strpos($file_contents, 'Main')) {
$array_pos = $count;
$main_file = $theme_files[$array_pos];
echo $main_file;
}
$count++;
}
}
This function causes the following error:
Notice: Undefined index: in /home/u841326920/public_html/includes/class.themes.php on line 30
I have narrowed the problem down the something wrong with the $count variable. Whenever I try and echo the $count value once the script has found the correct file, nothing is shown.
But after spending nearly an hour on such a simple problem, it is obviously starting to frustrate me, so I am now seeking help.
(Note: I directly copied the function directly from the old post into my code, and made the appropriate changes to variables to 'work' in the new site, so it's is pretty much exactly the same as the solution that fixed my previous problem - which funnily enough was also caused by the $count variable).
Thanks,
Kieron
You can use the foreach $key instead of a separate count variable, try the code below:
foreach($theme_files as $key => $file) {
$file_contents = file_get_contents($file);
if(strpos($file_contents, 'Main') !== false) {
$main_file = $theme_files[$key];
echo $main_file;
}
}
You are setting
$count = null;
, try to ++ it before the
$array_pos = $count;
I am trying to write a function that counts the image while looping through the files for one case (out of many cases), I declared the variable $imageCount global within the function copyInsertImage so that after the image is successfully inserted into the database, I do an $imageCount++.
After processing all the images for the case, the code exits the loop, the function processImages will be recalled again. However, I did a var_dump($imageCount) to print out the image count each time it gets increased by one, and found out that it was never reset back to 0 by the $imageCount = 0 when running the loop for a new case.
I am wondering if declaring global $imageCount has anything to do with it, because the code worked fine previously before grouping the same script into a function. If so, what is the solution?
Thanks!
function processImages($path,$patientID,$caseID)
{
// $path = rootDirectory/patientID
global $targetDirectory;
$targetPath = $targetDirectory.$patientID;
$caseSummaryFolder = $path."/".$caseID."/Summary";
$srcDirPath=$path."/".$caseID."/Summary/slicesdir"; //AKA src
$dstDirPath = $targetPath."/".$caseID;
//copyCaseImages($caseSummaryFolder,$targetCasePath,$patientID,$caseID);
global $status;
// print("processImages case path:".$casePath."</br>");
$files = glob($srcDirPath."/*.png");
echo "\n------------NEW CASE------------\n"
echo "PATIENT: $patientID \n";
echo "CASE: $caseID \n";
echo "--------------------------------\n"
$imageCount = 0;
for($i = 0; $i < count($files); $i++) {
$file = $files[$i];
$fileName = str_ireplace($srcDirPath."/", "", $file);
// if image name doesn't not contain string 'GROT'
if(strripos($fileName, "grot") === false)
{
if(doesImgExist($fileName)!==NULL) {
if (compareFileMTime($srcDirPath,$fileName,doesImgExist($fileName))) {
echo "There's a newer version of $fileName \n";
copyInsertImage($srcDirPath,$dstDirPath,$fileName,$patientID,$caseID);
}
else {
$imageCount++;
}
}
// copy image to analyzedCp and insert new image into DB
else {
copyInsertImage($srcDirPath,$dstDirPath,$fileName,$patientID,$caseID);
}
}
else {
echo "grot*.png files are not included \n";
}
}
If I understood your question correctly, it seems like you are redeclaring "global $imageCount" inside of your "copyInsertImage" function and this function is part of the for-loop. If this is indeed what you have then a problem is that everything time your for-loop hits the "copyInsertImage" function it will take re-declare $imageCount, this re-declaration will make imageCount a new variable and clear whatever you have stored in it. This could be the reason why you are seeing $imageCount = 0.
#andrewsi Answered my question.
My question was solved by declaring the initial $imageCount as global too.
"if you're using globals, you need to declare them as global in every function that uses them. Otherwise, you end up using a local variable with the same name. This is one of the reasons to try to avoid them if possible - it's easier to pass variables into functions when you need them."
Thanks!
So I have this app that processes CSV files. I have a line of code to load the file.
$myFile = "data/FrontlineSMS_Message_Export_20120721.csv"; //The name of the CSV file
$fh = fopen($myFile, 'r'); //Open the file
I would like to find a way in which I could look in the data directory and get the newest file (they all have date tags so they would be in order inside of data) and set the name equal to $myFile.
I really couldn't find and understand the documentation of php directories so any helpful resources would be appreciated as well. Thank you.
Here's an attempt using scandir, assuming the only files in the directory have timestamped filenames:
$files = scandir('data', SCANDIR_SORT_DESCENDING);
$newest_file = $files[0];
We first list all files in the directory in descending order, then, whichever one is first in that list has the "greatest" filename — and therefore the greatest timestamp value — and is therefore the newest.
Note that scandir was added in PHP 5, but its documentation page shows how to implement that behavior in PHP 4.
For a search with wildcard you can use:
<?php
$path = "/var/www/html/*";
$latest_ctime = 0;
$latest_filename = '';
$files = glob($path);
foreach($files as $file)
{
if (is_file($file) && filectime($file) > $latest_ctime)
{
$latest_ctime = filectime($file);
$latest_filename = $file;
}
}
return $latest_filename;
?>
My solution, improved solution from Max Hofmann:
$ret = [];
$dir = Yii::getAlias("#app") . "/web/uploads/problem-letters/{$this->id}"; // set directory in question
if(is_dir($dir)) {
$ret = array_diff(scandir($dir), array(".", "..")); // get all files in dir as array and remove . and .. from it
}
usort($ret, function ($a, $b) use ($dir) {
if(filectime($dir . "/" . $a) < filectime($dir . "/" . $b)) {
return -1;
} else if(filectime($dir . "/" . $a) == filectime($dir . "/" . $b)) {
return 0;
} else {
return 1;
}
}); // sort array by file creation time, older first
echo $ret[count($ret)-1]; // filename of last created file
Here's an example where I felt more confident in using my own validator rather than simply relying on a timestamp with scandir().
In this context, I want to check if my server has a more recent file version than the client's version. So I compare version numbers from the file names.
$clientAppVersion = "1.0.5";
$latestVersionFileName = "";
$directory = "../../download/updates/darwin/"
$arrayOfFiles = scandir($directory);
foreach ($arrayOfFiles as $file) {
if (is_file($directory . $file)) {
// Your custom code here... For example:
$serverFileVersion = getVersionNumberFromFileName($file);
if (isVersionNumberGreater($serverFileVersion, $clientAppVersion)) {
$latestVersionFileName = $file;
}
}
}
// function declarations in my php file (used in the forEach loop)
function getVersionNumberFromFileName($fileName) {
// extract the version number with regEx replacement
return preg_replace("/Finance D - Tenue de livres-darwin-(x64|arm64)-|\.zip/", "", $fileName);
}
function removeAllNonDigits($semanticVersionString) {
// use regex replacement to keep only numeric values in the semantic version string
return preg_replace("/\D+/", "", $semanticVersionString);
}
function isVersionNumberGreater($serverFileVersion, $clientFileVersion): bool {
// receives two semantic versions (1.0.4) and compares their numeric value (104)
// true when server version is greater than client version (105 > 104)
return removeAllNonDigits($serverFileVersion) > removeAllNonDigits($clientFileVersion);
}
Using this manual comparison instead of a timestamp I can achieve a more surgical result. I hope this can give you some useful ideas if you have a similar requirement.
(PS: I took time to post because I was not satisfied with the answers I found relating to the specific requirement I had. Please be kind I'm also not very used to StackOverflow - Thanks!)