Behat: Cannot kill web server process when done testing - php

I'm testing a web crawler script. I'm using the php builtin webserver to test against pages locally.
I can start the server but I cannot kill the process because it is already killed (I get the exception that I set Could not kill the testing web server).
Here is my attempt:
<?php
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Behat\Hook\Scope\AfterScenarioScope;
/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context, SnippetAcceptingContext
{
const TESTING_BASE_URL = 'http://127.0.0.1:6666';
const TESTING_DIR = '/tmp/testDirectory';
private $pid;
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
}
/**
* #BeforeScenario
*/
public function before(BeforeScenarioScope $scope)
{
// Create testing directory holding our pages
if (!is_dir(self::TESTING_DIR)) {
if (!mkdir(self::TESTING_DIR)) {
throw new \Exception('Cannot create the directory for testing');
}
}
// Start the testing server
$command = sprintf(
'php -S %s -t $%s >/dev/null 2>&1 & echo $!',
escapeshellarg(self::TESTING_BASE_URL),
escapeshellarg(self::TESTING_DIR)
);
$output = [];
exec($command, $output, $return_var);
if ($return_var !== 0) {
throw new \Exception('Cannot start the testing web server');
}
$this->pid = (int)$output[0];
echo sprintf(
'Testing web server started on %s with PID %s %s %s',
self::TESTING_BASE_URL,
(string)$this->pid,
PHP_EOL,
PHP_EOL
);
}
/**
* #AfterScenario
*/
public function after(AfterScenarioScope $scope)
{
// ... kill the web server
$output = [];
exec('kill ' . (string) $this->pid, $return_var);
if ($return_var !== 0) {
throw new \Exception('Could not kill the testing web server (PID ' . (string) $this->pid . ')');
}
echo 'Testing web server killed (PID ', (string) $this->pid, ')', PHP_EOL, PHP_EOL;
// ... remove the test directory
$o = [];
exec('rm -rf ' . escapeshellarg(self::TESTING_DIR), $o, $returnVar);
if ($returnVar !== 0) {
throw new \Exception('Cannot remove the testing directory');
}
}
// ...
}
I also tried various things like putting it all in the constructor, using register_shutdown_function, without any success.
What am I missing? Any idea on how I can solve this?
Instead of just "not caring about killing the server process" (because to me it looks like it's gone when I try to kill the process, hence the error, I can't find it when I issue ps aux | grep php on the command line after running behat), isn't it "cleaner" to kill it as I attend to?

The exec call is missing the output parameter:
exec('kill ' . (string) $this->pid, $output, $return_var);
Unless this is set, the exception will always be thrown, because $return_var is actually the output of the command (which is an array not an integer).

Related

How to add cron jobs dynamically by reading from file, irrespective of server

I want to add cron job dynamically once user install a php application on their server, like admin configuration, I need to set cron job dynamically once going thought its configuration settings in php?
I am using codeigniter to set the cron job, also I added the same from cpanel manually and it is working fine.
You can create a file in
/etc/cron.d
and use PHP's file_put_contents() to update the file. However, you'll need to elevate PHP or Apache's permissions (depending on your PHP handler). This isn't recommended since it leads to security issues.
If you're using Cron to run PHP scripts, then you can call the scripts directly using PHP every X amount of time. Create a variable in your database representing the last time your script was run. If X amount of time passed since the script was run, then you run the script and update the variable.
If the script takes a long time to execute, then use PHP to run it in a separate process, so the user doesn't have to wait for it to finish.
Thanks
First You have to create one txt file for example crontab.txt
then you have to use shell script like below
exec ( 'sudo crontab -u apache -r' );
file_put_contents ( '/var/www/html/YOUR_PROJECT/crontab.txt',"25 15 * * * php /var/www/html/YOUR_PROJECT/YOUR_CONTROLLER/YOUR_METHOD'.PHP_EOL);
And Lastly, you have to execute that file like
exec ( 'crontab /var/www/html/YOUR_PROJECT/crontab.txt' );
Hope this will help you.
you can do this using shell script
shell_exec('echo "25 15 * * * <path to php> /var/www/cronjob/helloworld.php > /var/www/cronjob/cron.log" | crontab -')
you can use these functions or class
class Crontab {
// In this class, array instead of string would be the standard input / output format.
// Legacy way to add a job:
// $output = shell_exec('(crontab -l; echo "'.$job.'") | crontab -');
static private function stringToArray($jobs = '') {
$array = explode("\r\n", trim($jobs)); // trim() gets rid of the last \r\n
foreach ($array as $key => $item) {
if ($item == '') {
unset($array[$key]);
}
}
return $array;
}
static private function arrayToString($jobs = array()) {
$string = implode("\r\n", $jobs);
return $string;
}
static public function getJobs() {
$output = shell_exec('crontab -l');
return self::stringToArray($output);
}
static public function saveJobs($jobs = array()) {
$output = shell_exec('echo "'.self::arrayToString($jobs).'" | crontab -');
return $output;
}
static public function doesJobExist($job = '') {
$jobs = self::getJobs();
if (in_array($job, $jobs)) {
return true;
} else {
return false;
}
}
static public function addJob($job = '') {
if (self::doesJobExist($job)) {
return false;
} else {
$jobs = self::getJobs();
$jobs[] = $job;
return self::saveJobs($jobs);
}
}
static public function removeJob($job = '') {
if (self::doesJobExist($job)) {
$jobs = self::getJobs();
unset($jobs[array_search($job, $jobs)]);
return self::saveJobs($jobs);
} else {
return false;
}
}
}
Hi thank you for the replies, i got my answers now, based on the replies
I tried to add a new cron job to crontab file using php (codeigniter):
following is my answer
$phppath = exec('which php');
$user_file_path = getcwd();
$cronjob1 = "0 0 1 * * $phppath $user_file_path/index.php
automatic_updates/leave_update_cron";
// run each cron job
// get all current cron jobs
$output = shell_exec('crontab -l');
// add our new job
file_put_contents('/tmp/crontab.txt', $output.$cronjob1.PHP_EOL);
// once append the job, execute the new file
exec('crontab /tmp/crontab.txt');
This will add a new cron job without deleting anything on the current cron job file

Crontab do not run the php command

I am using Symfony 3 framework with pheanstalk php library. I run the app on server with Linux Debian Jesse. The job creation works ok and if run the worker from terminal it works like it should. But when I added the command to crontab I see that the command do not work. There is not any log in /var/main/user to help me with debuging. I will be very glad for any help.
This is my worker command (only the execute function):
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln("\n<info>Beanstalk worker service started</info>");
$tubes = $this->getContainer()->get('app.job_manager.power_plant')->getTubes();
if ($input->getOption('one-check')) {
// run once
foreach ($tubes as $tubeName) {
if ($tubeName != "default") {
$this->getContainer()->get('app.queue_manager')->fetchQueue($tubeName);
}
}
$output->writeln("\n<info>Beanstalk worker completed check and stoped</info>");
} else {
// run forever
set_time_limit(0);
max_execution_time(0);
while (1) {
foreach ($tubes as $tubeName) {
if ($tubeName != "default") {
$this->getContainer()->get('app.queue_manager')->fetchQueue($tubeName);
}
}
}
$output->writeln("\n<info>Beanstalk worker stoped</info>");
}
}
This is my app.queue_manager function to get job from queue and run command:
public function fetchQueue($tubeName)
{
if ($this->pheanstalk->getConnection()->isServiceListening()) {
while (true === is_object($job = $this->pheanstalk->watch($tubeName)->ignore('default')->reserve(self::WATCH_TIMEOUT))) {
$data = json_decode($job->getData(), true);
$this->worker($data);
$this->pheanstalk->delete($job);
}
}
}
And the worker to run command
public function worker($data)
{
$application = new Application($this->kernel);
$application->setAutoExit(false);
$parameters = [];
$parameters['command'] = $data['command'];
foreach ($data['meta'] as $key => $param) {
$parameters[$key] = $param;
}
$input = new ArrayInput($parameters);
$output = new NullOutput();
return $application->run($input, $output);
}
This is my crontab that do not work:
#reboot /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start
I created another cron tab that works ok. It works every 15 min, and the difference is that do not have infinite loop (while(1)) so it goes only once thru tubes and than finished. But it is not what i want. I want infinite loop that works all the time like I created it with first crontab:
*/15 * * * * /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start --one-check
If it works in the console, it doesn't work for your file user permissions. Check it.
You can report your error by email with this job
*/15 * * * * /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start --one-check 2>&1 | mail -s "mysql_dump" example#mail.example
I used rc.local. It solve the problem. Tnx FreudianSlip

php shell_exec not working on windows

I am on Windows. I have a string as follows
Ra6tRR8DIsoll/7O5o6DBfRvlbhdj4mGmivRXYiDHD1JpY1WIMpI5HycXp/IsnUF
What I am trying to do is perform a shell_exec from php to my console app . The console app command line is as follows
consoleapp decrypt Ra6tRR8DIsoll/7O5o6DBfRvlbhdj4mGmivRXYiDHD1JpY1WIMpI5HycXp/IsnUF
However when I try to run shell_exec using the command above I get a null as a result. the command works on the console, though.
Is it because there are special characters in the string ? I was looking at escapeshellarg and wondering if it could help resolve this issue ?
following is my code . I am calling the doDecreyptWithTime method.
private function doDecryptWithTime($string)
{
$command = "aesencryptdecrypt.exe decrypt ". escapeshellarg(trim($string));
// dd($command);
return exec($command);
}
private function doDecrypt($string)
{
$command = $this->generateEntireCommand(EncryptionCommands::Decrypt). ' ' .$string;
return shell_exec($command);
// return explode(Config::get('myconfig.DecryptDelimiter'),$output);
}
private function generateEntireCommand($command)
{
if($command == EncryptionCommands::Encrypt)
return $this->generatePath(). ' '. EncryptionCommands::Encrypt;
elseif($command == EncryptionCommands::Decrypt)
return $this->generatePath(). ' '.EncryptionCommands::Decrypt;
elseif($command == EncryptionCommands::DecryptWithTime)
return $this->generatePath(). ' '.EncryptionCommands::DecryptWithTime;
}
private function generatePath()
{
$fullEncryptFileName = null;
$encryptFileName = Config::get('myconfig.EcryptionFileName');
if(!is_null(Config::get('myconfig.EcryptionFileName')))
{
$fullEncryptFileName = public_path().DIRECTORY_SEPARATOR.Config::get('myconfig.EncryptFileLocation').DIRECTORY_SEPARATOR.$encryptFileName;
}
else
{
$fullEncryptFileName = public_path().DIRECTORY_SEPARATOR.$this->fileLocation.DIRECTORY_SEPARATOR.$encryptFileName;
}
if(!is_null($fullEncryptFileName))
return $fullEncryptFileName;
else
throw new Exception('could not generate the filename');
}
Update 1 :
I am on php 5.5 so i Believe that the safe_mode functionality is not available any more. Also I have checked the disabled functions list, shell_exec, exec are not listed there .
please suggest!

How to start a PHP built in web server before running a test and close it after test has run

I am trying to use Behat for BDD testing. When running a build on Jenkins, I would like Behat to open PHP's build in web server and then close it after running the tests. How to do that?
Basically I need to run:
php -S localhost:8000
In my BDD tests I tried:
/**
* #Given /^I call "([^"]*)" with email and password$/
*/
public function iCallWithPostData($uri)
{
echo exec('php -S localhost:8000');
$client = new Guzzle\Service\Client();
$request = $client->post('http://localhost:8000' . $uri, array(), '{"email":"a","password":"a"}')->send();
$this->response = $request->getBody(true);
}
But then when running Behat it gets stuck without any message.
Just start the server as a part of your build process. Create an ant tasks which would start the server before behat is run and would kill it once behat is finished.
I've been successfully using this approach to start and stop the selenium server.
Solved this myself. I have create two methods. I call the first one before running my BDD tests and the second one after I ran the tests:
private function _startDevelopmentServer($pidfile)
{
$cmd = 'cd ../../public && php -S 127.0.0.1:8027 index.php';
$outputfile = '/dev/null';
shell_exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));
sleep(1);
}
private function _killDevelopmentServer($pidfile)
{
if (file_exists($pidfile)) {
$pids = file($pidfile);
foreach ($pids as $pid) {
shell_exec('kill -9 ' . $pid);
}
unlink($pidfile);
}
}

Using shell with Zend Framework

I'm curios if Zend has a component which can make use of the shell. For example I want to do an shell command like this:
mysqldump --compact --uroot --ppass mydatabase mydable >test.sql
from a controller.
If there isn't, do you know a way how to dump data from tables in Zend Framework?
update:
I've found a way here http://www.zfsnippets.com/snippets/view/id/68
There's no direct exec() support in the zend framework. the closest to command line support there is the Zend_Console class, but it's meant for getting arguments from the command line.
I would wrap the exec() function as a process object and work with that. Here's a nice example from the php docs:
<?php
// You may use status(), start(), and stop(). notice that start() method gets called automatically one time.
$process = new Process('ls -al');
// or if you got the pid, however here only the status() metod will work.
$process = new Process();
$process.setPid(my_pid);
?>
<?php
// Then you can start/stop/ check status of the job.
$process.stop();
$process.start();
if ($process.status()){
echo "The process is currently running";
}else{
echo "The process is not running.";
}
?>
<?php
/* An easy way to keep in track of external processes.
* Ever wanted to execute a process in php, but you still wanted to have somewhat controll of the process ? Well.. This is a way of doing it.
* #compability: Linux only. (Windows does not work).
* #author: Peec
*/
class Process{
private $pid;
private $command;
public function __construct($cl=false){
if ($cl != false){
$this->command = $cl;
$this->runCom();
}
}
private function runCom(){
$command = 'nohup '.$this->command.' > /dev/null 2>&1 & echo $!';
exec($command ,$op);
$this->pid = (int)$op[0];
}
public function setPid($pid){
$this->pid = $pid;
}
public function getPid(){
return $this->pid;
}
public function status(){
$command = 'ps -p '.$this->pid;
exec($command,$op);
if (!isset($op[1]))return false;
else return true;
}
public function start(){
if ($this->command != '')$this->runCom();
else return true;
}
public function stop(){
$command = 'kill '.$this->pid;
exec($command);
if ($this->status() == false)return true;
else return false;
}
}
?>
It's also let you stop and check the status of a job.

Categories