I am collecting a series of php files and testing to see if a single function returns valid output. To facilitate the process, all their functions are named identically. So then I can run:
foreach($fileList as $file) {
require($file);
echo $testFunction();
}
The problem is that php throws an error 'Cannot redeclare function' since the second file's function is named the same as the first. What I want to do is 'undeclare' a function after I test its output but I know this isn't possible and I'm trying to handle this procedurally. unlink($file) does not remove the instance of the function, unfortunately. Is there a simple way to handle this without using an OOP approach?
UPDATE #1
Using exec() instead of shell_exec() allows me to check err status (which is #2). CHMOD was necessary as user/group prevented execution (security settings on this offline server to be updated once the script is functioning). At this point, it does not echo anything since shell_exec() is returning an error (at least I think so since the output from shell_exec is empty and since exec is returning error #2). Here is an updated test:
$fileList = array('test.php');
foreach($fileList as $file) {
// load code from the current file into a $code variable,
// and append a call to the function held in the $testFunction variable
$code = file_get_contents($file) . "\n" . 'testFunction();';
// save this to a temporary file
file_put_contents('test-file.php', $code);
// execute the test file in a separate php process,
// storing the output in the $output variable for examination
//*************** */
$output=null;
$retval=null;
$absPath = realpath('test-file.php');
chmod($absPath,0777);
echo $absPath;
exec($absPath, $output, $retval);
echo "Returned with status $retval and output:\n";
print_r($output);
}
UPDATE #2
While you can't undeclare a function, you can repeatedly assign different functions to the same var. For example:
$listOfFunctionNames = array('function1', 'function2', 'function3);
foreach($listOfFunctionNames as $func) {
$funxion = $func;
$funxion();
}
You can execute the files in another process, for example (assuming $testFunction is defined in the files), you could do something like this (assuming you are running on Linux):
foreach($fileList as $file) {
// load code from the current file into a $code variable,
// and append a call to the function held in the $testFunction variable
$code = file_get_contents($file) . "\n" . '$testFunction();';
// save this to a temporary file
file_put_contents('/tmp/test-file.php', $code);
// execute the test file in a separate php process,
// storing the output in the $output variable for examination
$output = shell_exec('php /tmp/test-file.php');
// examine output as you wish
}
unlink('/tmp/test-file.php');
EDIT:
Since testFunction does not echo, and instead returns the output to be examined, we can simply modify the test file to echo testFunction();.
$code = file_get_contents($file) . "\n" . 'echo testFunction();'; // <- NOTE: the semi-colon after testFunction();
I noticed my original answer was lacking a semi-colon in the test file, which is probably where the error was coming from. What you can do to ensure it's correct is have this script generate the first test file and terminate early. You can then manually inspect the file for correctness and also use PHP to ensure it's parse-able, from the command line:
php -l /tmp/test-file.php
Note also there are more sophisticated ways you could check correctness of each test file, however I am trying to keep the answer concise, as that is starting to stray into a separate question.
Related
actual I finished writing my program. Because it is only a plugin and it runs on a external server I still want to see if I get some errors or something else in the console.
I wrote every console input with echo ...;. My question now is if it is possible to get the text of the console?
Because then I could easily safe it in a .txt file and could get access to it from the web :) - Or is there another way to get the console text?
I could probably just say fwrite(...) instand of echo ...;. But this will cost a lot of time...
Greetings and Thank You!
An alternative that could be usefull on windows would be to save all the output buffer to a txt, first check your php configuration for the console app implicit_flush must be off then
<?php
ob_start(); //before any echo
/** YOUR CODE HERE **/
$output = ob_get_contents(); //this variable has all the echoes
file_put_contents('c:\whatever.txt',$output);
ob_flush(); //shows the echoes on console
?>
If your goal is to create a text file to access, then you should create a text file directly.
(do this instead of echoing to console)
$output = $consoleData . "\n";
$output .= $moreConsoleData . "\n";
(Once you've completed that, just create the file:)
$file = fopen('output.txt', 'a');
fwrite($file, $output);
fclose($file);
Of course, this is sparse - you should also check that the file exists, create it if necessary, etc.
For console (commando line interface) you can redirect the output of your script:
php yourscript.php > path-of-your-file.txt
If you haven't access to a command line interface or to edit the cronjob line, you can duplicate the starndar output at the begining of the script:
$fdout = fopen('path-to-your-script.txt', 'wb');
eio_dup2($fdout, STDOUT);
eio_event_loop();
fclose($fdout);
(eio is an pecl extension)
If you are running the script using the console (i.e. php yourscript.php), you can easily save the output my modifying your command to:
php yourscript.php > path/to/log.txt
The above command will capture all output by the script and save it to log.txt. Change the paths for your script / log as required.
So i've got a python script, which is being executed using the exec() command through embedded PHP. Currently, the python script takes just one file (uploaded using a standard HTML form), but I want to pass it multiple files, and have it run through them every time. I've tried the following code based on some examples I saw here on StackOverflow, but can't seem to get it to work. Does anyone know how I can do it? I just need to get the tmp_file name and the name, then pass them to the exec function, I want it to run once for each file (with the respective information for each file).
<?php
foreach ($_FILES["inputFile"]{
$dataIn = $_FILES["inputFile"]["tmp_name"];
$originalName = $_FILES["inputFile"]["name"];
exec("python /home/will/public_html/OrderAnalyser.py $dataIn $originalName 2>&1",$output);
foreach ($output as $out){
echo $out;
echo "<br />";
}
}
?>
I have a perl script which takes input as a file and return results in text files. I want to use file as input, which is uploaded by user through php page. For that what should I do? I have PHP 5.3.14 and ActivePerl 5.14.x.
In PHP, when a file is uploaded, it is first placed in a temporary location. You can move it to another location using move_uploaded_file():
http://php.net/manual/en/function.move-uploaded-file.php
Then, you can call your Perl script in a variety of ways, which are outlined here:
How can I call a Perl script from PHP?
So, let's say you are using the low-level method of using the exec() function, and the file is uploaded in a file upload field with name "userfile", you might use something like this:
$perlCommand = // ... something, e.g. from config ...
$workingPath = // ... something, e.g. from config ...
$filename = $workingPath . $_FILES['userfile']['name']
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $filename)) {
$output = array();
$return = 0;
exec($perlCommand . ' ' . $filename, $output, $return);
// Do something with $output and / or $return values
}
Note that this assumes that the Perl script takes the name of the file as an argument. It might be that it reads the file from standard input, it wasn't clear from the question. Obviously if it is the latter then it will be a bit different, again depending on the method you use to call Perl.
Thanks #leftclickben
I've solved the problem. I used a form to get file from user and then saved the filename to an argument $file.
Then I passed $file to Perl script using
$result = shell_exec("path\to\perl.pl" $file);
echo $result;
$file passed to perl.pl as an array named $ARGV[0]
#!usr/bin/perl
$filename = $ARGV[0];
open(HDL, $filename) or die "file not available, restart program\n";
I have a cronjob system with PHP. I have a file named cron.php and wanna to check if it is not loaded, load it. it is very important to me that this file run only one time and I need the way how define it is already running. I can't use any system functions like exec,system,... do you have any solutions?
NOTE: I run my script trough CURL and include_one and require_once don't work for this case.
You could use flock() to lock the php file itself, like this:
<?php
class Lock{
private $fp;
function __construct(){
$this->fp=fopen(__FILE__,'r');
if (!flock($this->fp,LOCK_EX|LOCK_NB)) {
die('already running !'.PHP_EOL);
}
}
function __destruct(){
flock($this->fp,LOCK_UN);
fclose($this->fp);
}
}
$lock=new Lock();
// simulate some processing
sleep(60);
echo "END";
?>
Can you just use require_once or include_once?
require_once will throw a PHP fatal error (will stop execution) if the file cannot be evaluated.
include_once will throw a PHP warning (execution may continue).
// Require tasks.php to run once
require_once 'path/to/tasks.php';
// Attempt to run tasks.php and only once
include_once 'path/to/tasks.php';
Your problem is essentially equivalent to "Check if a php script is still running"
Please refer this
Check if a php script is still running
if I understand you correctly, you want to prevent your cron.php script from getting started a second time by cron, it is not called from another PHP script? (in that case, require_once would be the right answer)
as I understand it, you need to store a marker that indicates that your script is running and remove that marker at the end of your script.
depending on your environment, you could either create a small file, i.e. .lock or store a status = locked entry in your database.
edit: here is a small code example using the file method:
<?php
// cron.php
$path = '/path/to/your/data/directory/';
if (file_exists($path . '.lock') {
die('cron.php is already running');
}
// if script reaches this point, it is not locked -> create a lock
file_put_contents($path . '.lock', 'lockfile created at ' . now());
//... your code....
//unlocking
unlink($path . '.lock');
?>
If you are using cURL then I believe your are using cURL to request a page such as http://domain.com/cron.php. The machine requesting the script via cURL/wget/browser/etc has no way of knowing if the script is already being executed on the server. However, you can configure your cron.php script to run only once:
<?php
// attempt to obtain a lock
$fp = fopen(basename(__FILE__) . ".lock", "w");
if (flock($fp, LOCK_EX | LOCK_NB) === false) {
echo basename(__FILE__) . ": already running\n";
exit(-1);
}
// code goes here
echo date("Y-m-d H:i:s") . ": cron job started\n";
sleep(30);
echo date("Y-m-d H:i:s") . ": cron job ended\n";
// release the lock
flock($fp, LOCK_UN);
fclose($fp);
The sample code uses PHP flock function. The LOCK_EX flag tells PHP that it needs to obtain an exclusive lock; i.e. no other process is allowed to access the file. The LOCK_NB tells PHP that it should not block (wait for the lock to be released) and return false immediately. Together, the two switches assure that a second process cannot lock the file while the first one has it locked.
you can use require_once or include_once
The general syntax of both the include and require statements are as follows:
include "file_to_load.php";
include_once "file_to_load.php";
When the include_once/require_once statements are used, the file cannot be loaded or executed multiple times. If an attempt is made to load a file twice using one of these two methods, it will be ignored. Because it is unacceptable to define the same function multiple times within a script, these functions allow the developer to include a script as needed without having to check whether it has been previously loaded.
NOTE The capability to return values from external files is limited only to the include_once statements. The require_once statements cannot be used in this fashion.
include_once('class.php');
php.net states
The include_once statement includes and evaluates the specified file
during the execution of the script. This is a behavior similar to the
include statement, with the only difference being that if the code
from a file has already been included, it will not be included again.
As the name suggests, it will be included just once.
You can use either require_once or include_once.
if you are confuse what to use then difference between this two is that
require_once will stop executing preceding code, trwos fatal error, if file mentioned is not found
so if you want preceding code to continue even file is not found then don't use it.
where as the include_once will continue executing preceding code.
You can use Database for this case, make an entry of the page in database and second column for checking whether is it loaded or not (eg. '0' if not loaded yet and '1' it is loaded). Initially keep value of that row as '0' when the page is loaded update that column as '1'.
If the code is the same, there appears to be a difference between:
include 'external.php';
and
eval('?>' . file_get_contents('external.php') . '<?php');
What is the difference? Does anybody know?
I know the two are different because the include works fine and the eval gives an error. When I originally asked the question, I wasn't sure whether it gave an error on all code or just on mine (and because the code was evaled, it was very hard to find out what the error meant). However, after having researched the answer, it turns out that whether or not you get the error does not depend on the code in the external.php, but does depend on your php settings (short_open_tag to be precise).
After some more research I found out what was wrong myself. The problem is in the fact that <?php is a "short opening tag" and so will only work if short_open_tag is set to 1 (in php.ini or something to the same effect). The correct full tag is <?php, which has a space after the second p.
As such the proper equivalent of the include is:
eval('?>' . file_get_contents('external.php') . '<?php ');
Alternatively, you can leave the opening tag out all together (as noted in the comments below):
eval('?>' . file_get_contents('external.php'));
My original solution was to add a semicolon, which also works, but looks a lot less clean if you ask me:
eval('?>' . file_get_contents('external.php') . '<?php;');
AFAIK you can't take advantage of php accelerators if you use eval().
If you are using a webserver on which you have installed an opcode cache, like APC, eval will not be the "best solution" : eval'd code is not store in the opcode cache, if I remember correctly (and another answer said the same thing, btw).
A solution you could use, at least if the code is not often changed, is get a mix of code stored in database and included code :
when necessary, fetch the code from DB, and store it in a file on disk
include that file
as the code is now in a file, on disk, opcode cache will be able to cache it -- which is better for performances
and you will not need to make a request to the DB each time you have to execute the code.
I've worked with software that uses this solution (the on-disk file being no more than a cache of the code stored in DB), and I worked not too bad -- way better that doing loads of DB requests of each page, anyway...
Some not so good things, as a consequence :
you have to fetch the code from the DB to put it in the file "when necessary"
this could mean re-generating the temporary file once every hour, or deleting it when the entry in DB is modified ? Do you have a way to identify when this happens ?
you also have to change your code, to use the temporary file, or re-generate it if necessary
if you have several places to modifiy, this could mean some work
BTW : would I dare saying something like "eval is evil" ?
This lets you include a file assuming file wrappers for includes is on in PHP:
function stringToTempFileName($str)
{
if (version_compare(PHP_VERSION, '5.1.0', '>=') && strlen($str < (1024 * 512))) {
$file = 'data://text/plain;base64,' . base64_encode($str);
} else {
$file = Utils::tempFileName();
file_put_contents($file, $str);
}
return $file;
}
... Then include that 'file.' Yes, this will also disable opcode caches, but it makes this 'eval' the same as an include with respect to behavior.
As noted by #bwoebi in this answer to my question, the eval substitution does not respect the file path context of the included file. As a test case:
Baz.php:
<?php return __FILE__;
Foo.php:
<?php
echo eval('?>' . file_get_contents('Baz.php', FILE_USE_INCLUDE_PATH)) . "\n";
echo (include 'Baz.php') . "\n";
Result of executing php Foo.php:
$ php Foo.php
/path/to/file/Foo.php(2) : eval()'d code
/path/to/file/Baz.php
I don't know of any way to change the __FILE__ constant and friends at runtime, so I do not think there is any general way to define include in terms of eval.
Only eval('?>' . file_get_contents('external.php')); variant is correct replacement for include.
See tests:
<?php
$includes = array(
'some text',
'<?php print "some text"; ?>',
'<?php print "some text";',
'some text<?php',
'some text<?php ',
'some text<?php;',
'some text<?php ?>',
'<?php ?>some text',
);
$tempFile = tempnam('/tmp', 'test_');
print "\r\n" . "Include:" . "\r\n";
foreach ($includes as $include)
{
file_put_contents($tempFile, $include);
var_dump(include $tempFile);
}
unlink($tempFile);
print "\r\n" . "Eval 1:" . "\r\n";
foreach ($includes as $include)
var_dump(eval('?>' . $include . '<?php '));
print "\r\n" . "Eval 2:" . "\r\n";
foreach ($includes as $include)
var_dump(eval('?>' . $include));
print "\r\n" . "Eval 3:" . "\r\n";
foreach ($includes as $include)
var_dump(eval('?>' . $include . '<?php;'));
Output:
Include:
some textint(1)
some textint(1)
some textint(1)
some text<?phpint(1)
some textint(1)
some text<?php;int(1)
some textint(1)
some textint(1)
Eval 1:
some textNULL
some textNULL
bool(false)
some text<?phpNULL
bool(false)
some text<?php;NULL
some textNULL
some textNULL
Eval 2:
some textNULL
some textNULL
some textNULL
some text<?phpNULL
some textNULL
some text<?php;NULL
some textNULL
some textNULL
Eval 3:
some text<?php;NULL
some text<?php;NULL
bool(false)
some text<?php<?php;NULL
bool(false)
some text<?php;<?php;NULL
some text<?php;NULL
some text<?php;NULL
Some thoughts about the solutions above:
Temporary file
Don't. It's very bad for performance, just don't do it. Not only does it drive your opcode cache totally crazy (cache hit never happens + it tries to cache it again every time) but also gives you the headache of filesystem locking under high (even moderate) loads, as you have to write the file and Apache/PHP has to read it.
Simple eval()
Acceptable in rare cases; don't do it too often. Indeed it's not cached (poor opcode cache just doesn't know it's the same string as before); at the same time, if your code is changing each time, eval is A LOT BETTER than include(), mostly because include() fills up the opcode cache on each call. Just like the tempfile case. It's horrible (~4x slower).
In-memory eval()
Actually, eval is very fast when your script is already in the string; most of the time it's the disk operation that pulls it back, now surely this depends on what you do in the script but in my very-small-script case, it was ~400 times faster. (Do you have memcached? Just thinking loud) So what include() can't do is evaluate the same thing twice without file operation, and this is very important. If you use it for ever-changing, small, memory-generated strings, obviously it's eval to choose - it's many-many times faster to load once + eval again and again than an iterated include().
TL;DR
Same code, once per request: include
Same code, several calls per request: eval
Varying code: eval
here is my approach.
it creates temporary php file and includes it.
but this way if code you want to run on this function has errors program exits before removing temporary file
so i make an autoclean procedure in function. this way it cleans old temporary files by an timeout everytime function runs. you can set timeout or disable it from options at start of function
i also added ignore error option for solving non removed temporary files. if errors ignored, program will continue and remove temporary file.
also some projects have to disable autoclean because it scans whole directory everytime it runs. it could hurt disk performance.
function eval2($c) {
$auto_clean_old_temporary_files=false; //checks old temporary eval2 files for this spesific temporary file names generated by settings below
$ignore_all_errors=true; //if you ignore errors you can remove temporary files even there is an error
$tempfiledirectory=''; //temporary file directory
$tempfileheader='eval2_'; // temporary file header
$tempfiletimeseperator='__'; // temporary file seperator for time
$tempfileremovetimeout=200; // temp file cleaning time in seconds
if ($auto_clean_old_temporary_files===true) {
$sd=scandir('.'); //scaning for old temporary files
foreach ($sd as $sf) {
if (strlen($sf)>(32+strlen($tempfileheader)+strlen($tempfiletimeseperator)+3)) { // if filename long enough
$t1=substr($sf,(32+strlen($tempfileheader)),strlen($tempfiletimeseperator)); //searching time seperator
$t2=substr($sf,0,strlen($tempfileheader)); //searching file header
if ($t1==$tempfiletimeseperator && $t2==$tempfileheader) { //checking for timeseperator and file name header
$ef=explode('.',$sf);
unset($ef[count($ef)]);//removing file extension
$nsf=implode('.',$ef);//joining file name without extension
$ef=explode($tempfiletimeseperator,$nsf);
$tm=(int)end($ef); //getting time from filename
$tmf=time()-$tm;
if ($tmf>$tempfileremovetimeout && $tmf<123456 && $tmf>0) { // if time passed more then timeout and difference with real time is logical
unlink($sf); // finally removing temporary file
}
}
}
}
}
$n=$tempfiledirectory.$tempfileheader . md5(microtime().rand(0,5000)). $tempfiletimeseperator . time() .'.php'; //creating spesific temporary file name
$c='<?php' . PHP_EOL . $c . PHP_EOL; //generating php content
file_put_contents($n,$c); //creating temporary file
if ($ignore_all_errors===true) { // including temporary file by your choise
$s=#include($n);
}else{
$s=include($n);
}
return $s;
}