Communicating between a C- and a PHP-Program - php

I'm writing a program for my Raspberry Pi, that consists of two main parts:
A C-Program that uses the Spotify-API "Libspotify" to search for music and for playing it.
A PHP-program, running on an apache2-Webserver, to control the Spotify-program from a PC or Smartphone in my local network.
The communication between these two separate programs works over a couple of files.
The C-part for receiving the user inputs is being called once a second and works like this:
void get_input() {
int i = 0, c;
FILE *file;
struct stat stat;
char buffer[INPUT_BUFFER_SIZE];
file = fopen(PATH_TO_COMMUNICATION, "r");
if (file == NULL) {
perror("Error opening PATH_TO_COMMUNICATION");
return;
}
fstat(fileno(file), &stat);
if (stat.st_size > 1) {
while((c = fgetc(file)) != EOF && i < INPUT_BUFFER_SIZE) {
buffer[i] = c;
++i;
}
buffer[i] = '\0';
parse_input(buffer);
}
fclose(file);
clear_file(PATH_TO_COMMUNICATION);
}
So, via fwrite() in PHP, I can send commands to the C-program.
Afterwards, the C-Program parses the input and writes the results in an "results"-file. Once this is done, I write the last contents of the "communication"-file an "last_query"-file, so in PHP i can see, when the entire results are written in the "results":
function search($query) {
write_to_communication("search ".$query);
do { // Wait for results
$file = fopen("../tmp/last_query", "r");
$line = fgets($file);
fclose($file);
time_nanosleep(0, 100000000);
} while($line != $query);
echo get_result_json();
}
It does work already, but I don't like the way at all. There is a lot of polling and unneccessary opening and closing of different files. Also, in the worst case the program needs more than a second until it starts to do something with the user input. Furthermore, there can be race conditions, when the C-program tries to read the file during the PHP-program writes into it.
So, now to my question: What is the "right" way, to achieve a nice and clean communication between the two program parts? Is there some completely different way without the ugly polling and without race conditions? Or can I improve the existing code, so that it gets nicer?

I suppose that you write both the PHP and the C code yourself, and that you have access to compilers and so on? What I would do is not at all start up a different process and use inter-process-communication, but write a PHP C++ extension that does this all for you.
The extension starts up a new thread, and this thread picks up all instruction from an in-memory instruction queue. When you're ready to pick up the result, the final instruction is sent to the thread (the instruction to close down), and when the thread is finally finished, you can pick up the result.
You could use a program like this:
#include <phpcpp.h>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
/**
* Class that takes instructions from PHP, and that executes them in CPP code
*/
class InstructionQueue : public Php::Base
{
private:
/**
* Queue with instructions (for simplicity, we store the instructions
* in strings, much cooler implementations are possible)
*
* #var std::queue
*/
std::queue<std::string> _queue;
/**
* The final result
* #var std::string
*/
std::string _result;
/**
* Counter with number of instructions
* #var int
*/
int _counter = 0;
/**
* Mutex to protect shared data
* #var std::mutex
*/
std::mutex _mutex;
/**
* Condition variable that the thread uses to wait for more instructions
* #var std::condition_variable
*/
std::condition_variable _condition;
/**
* Thread that processes the instructions
* #var std::thread
*/
std::thread _thread;
/**
* Procedure to execute one specific instruction
* #param instruction
*/
void execute(const std::string &instruction)
{
// #todo
//
// add your own the implementation, for now we just
// add the instruction to the result, and sleep a while
// to pretend that this is a difficult algorithm
// append the instruction to the result
_result.append(instruction);
_result.append("\n");
// sleep for a while
sleep(1);
}
/**
* Main procedure that runs the thread
*/
void run()
{
// need the mutex to access shared resources
std::unique_lock<std::mutex> lock(_mutex);
// keep looping
while (true)
{
// go wait for instructions
while (_counter == 0) _condition.wait(lock);
// check the number of instructions, leap out when empty
if (_queue.size() == 0) return;
// get instruction from the queue, and reduce queue size
std::string instruction(std::move(_queue.front()));
// remove front item from queue
_queue.pop();
// no longer need the lock
lock.unlock();
// run the instruction
execute(instruction);
// get back the lock for the next iteration of the main loop
lock.lock();
}
}
public:
/**
* C++ constructor
*/
InstructionQueue() : _thread(&InstructionQueue::run, this) {}
/**
* Copy constructor
*
* We just create a brand new queue when it is copied (copy constructor
* is required by the PHP-CPP library)
*
* #param queue
*/
InstructionQueue(const InstructionQueue &queue) : InstructionQueue() {}
/**
* Destructor
*/
virtual ~InstructionQueue()
{
// stop the thread
stop();
}
/**
* Method to add an instruction
* #param params Object representing PHP parameters
*/
void add(Php::Parameters &params)
{
// first parameter holds the instruction
std::string instruction = params[0];
// need a mutex to access shared resources
_mutex.lock();
// add instruction
_queue.push(instruction);
// update instruction counter
_counter++;
// done with shared resources
_mutex.unlock();
// notify the thread
_condition.notify_one();
}
/**
* Method to stop the thread
*/
void stop()
{
// is the thread already finished?
if (!_thread.joinable()) return;
// thread is still running, send instruction to stop (which is the
// same as not sending an instruction at all but just increasing the
// instruction counter, lock mutex to access protected data
_mutex.lock();
// add instruction
_counter++;
// done with shared resources
_mutex.unlock();
// notify the thread
_condition.notify_one();
// wait for the thread to finish
_thread.join();
}
/**
* Retrieve the result
* #return string
*/
Php::Value result()
{
// stop the thread first
stop();
// return the result
return _result;
}
};
/**
* Switch to C context to ensure that the get_module() function
* is callable by C programs (which the Zend engine is)
*/
extern "C" {
/**
* Startup function that is called by the Zend engine
* to retrieve all information about the extension
* #return void*
*/
PHPCPP_EXPORT void *get_module() {
// extension object
static Php::Extension myExtension("InstructionQueue", "1.0");
// description of the class so that PHP knows
// which methods are accessible
Php::Class<InstructionQueue> myClass("InstructionQueue");
// add methods
myClass.method("add", &InstructionQueue::add);
myClass.method("result", &InstructionQueue::result);
// add the class to the extension
myExtension.add(std::move(myClass));
// return the extension
return myExtension;
}
}
You can use this instruction queue from a PHP script like this:
<?php
$queue = new InstructionQueue();
$queue->add("instruction 1");
$queue->add("instruction 2");
$queue->add("instruction 3");
echo($queue->result());
As an example I've only added a silly implementation that sleeps for a while, but you could run your API-calling functions in there.
The extension uses the PHP-CPP library (see http://www.php-cpp.com).

Related

PHP cURL Timing Issue

I have a PHP script that is used to query an API and download some JSON information / insert that information into a MySQL database, we'll call this scriptA.php. I need to run this script multiple times as minute, preferably as many times in a minute that I can without allowing two instances to run at the same exact time or with any overlap. My solution to this has been to create scriptB.php and put in on a one minute cron job. Here is the source code of scriptB.php...
function next_run()
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "http://somewebsite.com/scriptA.php");
curl_exec($curl);
curl_close($curl);
unset($curl);
}
$i = 0;
$times_to_run = 7;
$function = array();
while ($i++ < $times_to_run) {
$function = next_run();
sleep(3);
}
My question at this point is to how cURL performs when used in a loop, does this code trigger scriptA.php and THEN once it has finished loading it at that point start the next cURL request? Does the 3 second sleep even make a difference or will this literally run as fast as the time it takes each cURL request to complete. My objective is to time this script and run it as many times as possible in a one minute window without two iterations of it being run at the same time. I don't want to include the sleep statement if it is not needed. I believe what happens is cURL will run each request upon finishing the last, if I am wrong is there someway that I can instruct it to do this?
preferably as many times in a minute that I can without allowing two instances to run at the same exact time or with any overlap. - then you shouldn't use a cronjob at all, you should use a daemon. but if you for some reason have to use a cronjob (eg, if you're on a shared webhosting platform that doesn't allow daemons), guess you could use the sleep hack to run the same code several times a minute?
* * * * * /usr/bin/php /path/to/scriptA.php
* * * * * sleep 10; /usr/bin/php /path/to/scriptA.php
* * * * * sleep 20; /usr/bin/php /path/to/scriptA.php
* * * * * sleep 30; /usr/bin/php /path/to/scriptA.php
* * * * * sleep 40; /usr/bin/php /path/to/scriptA.php
* * * * * sleep 50; /usr/bin/php /path/to/scriptA.php
should make it execute every 10 seconds.
as for making sure it doesn't run in paralell if the previous execution hasn't finished yet, add this to the start of scriptA
call_user_func ( function () {
static $lock;
$lock = fopen ( __FILE__, "rb" );
if (! flock ( $lock, LOCK_EX | LOCK_NB )) {
// failed to get a lock, probably means another instance is already running
die ();
}
register_shutdown_function ( function () use (&$lock) {
flock ( $lock, LOCK_UN );
} );
} );
and it will just die() if another instance of scriptA is already running. however, if you want it to wait for the previous execution to finish, instead of just exiting, remove LOCK_NB... but that could be dangerous, if every, or even just a majority of the executions use more than 10 seconds, you'll have more and more processes waiting for the previous execution to finish, until you run out of ram.
as for your curl questions,
My question at this point is to how cURL performs when used in a loop, does this code trigger scriptA.php and THEN once it has finished loading it at that point start the next cURL request, that is correct, curl waits until the page has completely loaded, usually meaning the entire scriptA has completed. (you can tell scriptA to finish the pageload prematurely with the fastcgi_finish_request() function if you really want, but that's unusual)
Does the 3 second sleep even make a difference or will this literally run as fast as the time it takes each cURL request to complete - yes, the sleep will make the loop 3 seconds slower per iteration.
My objective is to time this script and run it as many times as possible in a one minute window without two iterations of it being run at the same time - then make it a daemon that never exits, rather than a cronjob.
I don't want to include the sleep statement if it is not needed. - it's not needed.
I believe what happens is cURL will run each request upon finishing the last - this is correct.
I need to run this script multiple times as minute, preferably as many times in a minute that I can without allowing two instances to run
Your in luck as I wrote a class to handle just such a thing. You can find it on my github here
https://github.com/ArtisticPhoenix/MISC/blob/master/ProcLock.php
I'll also copy the full code at the end of this post.
The basic idea is to create a file, I will call it afile.lock for this example. In this file is recorded the PID, or the process ID of the current process that is ran by cron. Then when cron attempts to run the process again, it checks this lock file and sees if there is a PHP process running that is using this PID.
if there is it updates the modified time of the file (and throws an exception)
if there is not then you are free to create a new instance of the "worker".
As a bonus th modified time of the lock file can be used by the script (whose PID we are tracking) as a way of shutting down in the event the file is not updated, so for example: if cron is stopped, or if the lock file is manually deleted you can set in in such a way that the running script will detect this and self destruct.
So not only can you keep multiple instances from running, you can tell the current instance to die if cron is turned off.
The basic usage is as follows. In the cron file that starts up the "worker"
//define a lock file (this is actually optional)
ProcLock::setLockFile(__DIR__.'/afile.lock');
try{
//if you didn't set a lock file you can pass it in with this method call
ProcLock::lock();
//execute your process
}catch(\Exception $e){
if($e->getCode() == ProcLock::ALREADY_LOCKED){
//just exit or what have you
}else{
//some other exception happened.
}
}
It's basically that easy.
Then in the running process you can every so often check (for example if you have a loop that runs something)
$expires = 90; //1 1/2 minute (you may need a bit of fudge time)
foreach($something as $a=>$b){
$lastAccess = ProcLock::getLastAccess()
if(false == $lastAccess || $lastAccess + $expires < time()){
//if last access is false (no lock file)
//or last access + expiration, is less then the current time
//log something like killed by lock timeout
exit();
}
}
Basically what this says is that either the lock file was deleted wile the process was running, or cron failed to update it before the expiration time. So here we are giving it 90 seconds and cron should be updating the lock file every 60 seconds. As I said the lock file is updated automatically if it's found when calling lock(), which calls canLock() which if it returns true meaning we can lock the process because its not currently locked, then it runs touch($lockfile) which updates the mtime (modified time).
Obviously you can only self kill the process in this way if it is actively checking the access and expiration times.
This script is designed to work both on windows and linux. On windows under certain circumstances the lock file won't properly be deleted (sometimes when hitting ctrl+c in the CMD window), however I have taken great pains to make sure this does not happen, so the class file contains a custom register_shutdown_function that runs when the PHP script ends.
When running something using the ProcLoc in the browser please note that the process id will always be the same no matter the tab its ran in. So if you open one tab that is Process locked, then open another tab, the process locker will see it as the same process and allow it to lock again. To properly run it in a browser and test the locking it must be done using two separate browsers such as crome and firefox. It's not really intended to be ran in the browser but this is one quirk I noticed.
One last note this class is completely static, as you can have only one Process ID per process that is running, which should be obvious.
The tricky parts are
making sure the lock file is disposed of in the event of even critical PHP failures
making sure another process didn't pick up the pid number when it was freed from PHP. This can be done with relative accuracy, in that we can tell if a PHP process is using it, and if so we assume its the process we need, there is much less chance a re-used PID would show up for another process very quickly, even less that it would be another PHP process
making all this work on both Linux and Windows
Lucky for you I have already invested sufficient time in this to do all these things, this is a more generic version of an original lock script I made for my job that we have used in this way successfully for 3 years in maintaining control over various synchronous cron jobs, everything from sFTP upload scanning, expired file clean up to RabbitMq message workers that run for an indefinite period of time.
In anycase here is the full code, enjoy.
<?php
/*
(c) 2017 ArtisticPhoenix
For license information please view the LICENSE file included with this source code GPL3.0.
Proccess Locker
==================================================================
This is a pseudo implementation of mutex since php does not have
any thread synchronization objects
This class uses files to provide locking functionality.
Lock will be released in following cases
1 - user calls unlock
2 - when this lock object gets deleted
3 - when request or script ends
4 - when pid of lock does not match self::$_pid
==================================================================
Only one Lock per Process!
-note- when running in a browser typically all tabs will have the same PID
so the locking will not be able to tell if it's the same process, to get
around this run in CLI, or use 2 diffrent browsers, so the PID numbers are diffrent.
This class is static for the simple fact that locking is done per-proces, so there is no need
to ever have duplate ProcLocks within the same process
---------------------------------------------------------------
*/
final class {
/**
* exception code numbers
* #var int
*/
const DIRECTORY_NOT_FOUND = 2000;
const LOCK_FIRST = 2001;
const FAILED_TO_UNLOCK = 2002;
const FAILED_TO_LOCK = 2003;
const ALREADY_LOCKED = 2004;
const UNKNOWN_PID = 2005;
const PROC_UNKNOWN_PID = 2006;
/**
* process _key
* #var string
*/
protected static $_lockFile;
/**
*
* #var int
*/
protected static $_pid;
/**
* No construction allowed
*/
private function __construct(){}
/**
* No clones allowed
*/
private function __clone(){}
/**
* globaly sets the lock file
* #param string $lockFile
*/
public static function setLockFile( $lockFile ){
$dir = dirname( $lockFile );
if( !is_dir( dirname( $lockFile ))){
throw new Exception("Directory {$dir} not found", self::DIRECTORY_NOT_FOUND); //pid directroy invalid
}
self::$_lockFile = $lockFile;
}
/**
* return global lockfile
*/
public static function getLockFile() {
return ( self::$_lockFile ) ? self::$_lockFile : false;
}
/**
* safe check for local or global lock file
*/
protected static function _chk_lock_file( $lockFile = null ){
if( !$lockFile && !self::$_lockFile ){
throw new Exception("Lock first", self::LOCK_FIRST); //
}elseif( $lockFile ){
return $lockFile;
}else{
return self::$_lockFile;
}
}
/**
*
* #param string $lockFile
*/
public static function unlock( $lockFile = null ){
if( !self::$_pid ){
//no pid stored - not locked for this process
return;
}
$lockFile = self::_chk_lock_file($lockFile);
if(!file_exists($lockFile) || unlink($lockFile)){
return true;
}else{
throw new Exception("Failed to unlock {$lockFile}", self::FAILED_TO_UNLOCK ); //no lock file exists to unlock or no permissions to delete file
}
}
/**
*
* #param string $lockFile
*/
public static function lock( $lockFile = null ){
$lockFile = self::_chk_lock_file($lockFile);
if( self::canLock( $lockFile )){
self::$_pid = getmypid();
if(!file_put_contents($lockFile, self::$_pid ) ){
throw new Exception("Failed to lock {$lockFile}", self::FAILED_TO_LOCK ); //no permission to create pid file
}
}else{
throw new Exception('Process is already running[ '.$lockFile.' ]', self::ALREADY_LOCKED );//there is a process running with this pid
}
}
/**
*
* #param string $lockFile
*/
public static function getPidFromLockFile( $lockFile = null ){
$lockFile = self::_chk_lock_file($lockFile);
if(!file_exists($lockFile) || !is_file($lockFile)){
return false;
}
$pid = file_get_contents($lockFile);
return intval(trim($pid));
}
/**
*
* #return number
*/
public static function getMyPid(){
return ( self::$_pid ) ? self::$_pid : false;
}
/**
*
* #param string $lockFile
* #param string $myPid
* #throws Exception
*/
public static function validatePid($lockFile = null, $myPid = false ){
$lockFile = self::_chk_lock_file($lockFile);
if( !self::$_pid && !$myPid ){
throw new Exception('no pid supplied', self::UNKNOWN_PID ); //no stored or injected pid number
}elseif( !$myPid ){
$myPid = self::$_pid;
}
return ( $myPid == self::getPidFromLockFile( $lockFile ));
}
/**
* update the mtime of lock file
* #param string $lockFile
*/
public static function canLock( $lockFile = null){
if( self::$_pid ){
throw new Exception("Process was already locked", self::ALREADY_LOCKED ); //process was already locked - call this only before locking
}
$lockFile = self::_chk_lock_file($lockFile);
$pid = self::getPidFromLockFile( $lockFile );
if( !$pid ){
//if there is a not a pid then there is no lock file and it's ok to lock it
return true;
}
//validate the pid in the existing file
$valid = self::_validateProcess($pid);
if( !$valid ){
//if it's not valid - delete the lock file
if(unlink($lockFile)){
return true;
}else{
throw new Exception("Failed to unlock {$lockFile}", self::FAILED_TO_UNLOCK ); //no lock file exists to unlock or no permissions to delete file
}
}
//if there was a valid process running return false, we cannot lock it.
//update the lock files mTime - this is usefull for a heartbeat, a periodic keepalive script.
touch($lockFile);
return false;
}
/**
*
* #param string $lockFile
*/
public static function getLastAccess( $lockFile = null ){
$lockFile = self::_chk_lock_file($lockFile);
clearstatcache( $lockFile );
if( file_exists( $lockFile )){
return filemtime( $lockFile );
}
return false;
}
/**
*
* #param int $pid
*/
protected static function _validateProcess( $pid ){
$task = false;
$pid = intval($pid);
if(stripos(php_uname('s'), 'win') > -1){
$task = shell_exec("tasklist /fi \"PID eq {$pid}\"");
/*
'INFO: No tasks are running which match the specified criteria.
'
*/
/*
'
Image Name PID Session Name Session# Mem Usage
========================= ======== ================ =========== ============
php.exe 5064 Console 1 64,516 K
'
*/
}else{
$cmd = "ps ".intval($pid);
$task = shell_exec($cmd);
/*
' PID TTY STAT TIME COMMAND
'
*/
}
//print_rr( $task );
if($task){
return ( preg_match('/php|httpd/', $task) ) ? true : false;
}
throw new Exception("pid detection failed {$pid}", self::PROC_UNKNOWN_PID); //failed to parse the pid look up results
//this has been tested on CentOs 5,6,7 and windows 7 and 10
}
/**
* destroy a lock ( safe unlock )
*/
public static function destroy($lockFile = null){
try{
$lockFile = self::_chk_lock_file($lockFile);
self::unlock( $lockFile );
}catch( Exception $e ){
//ignore errors here - this called from distruction so we dont care if it fails or succeeds
//generally a new process will be able to tell if the pid is still in use so
//this is just a cleanup process
}
}
}
/*
* register our shutdown handler - if the script dies unlock the lock
* this is superior to __destruct(), because the shutdown handler runs even in situation where PHP exhausts all memory
*/
register_shutdown_function(array('\\Lib\\Queue\\ProcLock',"destroy"));

Laravel schedular: execute a command every second

I have a project that needs to send notifications via WebSockets continuously. It should connect to a device that returns the overall status in string format. The system processes it and then sends notifications based on various conditions.
Since the scheduler can repeat a task as early as a minute, I need to find a way to execute the function every second.
Here is my app/Console/Kernel.php:
<?php
...
class Kernel extends ConsoleKernel
{
...
protected function schedule(Schedule $schedule)
{
$schedule->call(function(){
// connect to the device and process its response
})->everyMinute();
}
}
PS: If you have a better idea to handle the situation, please share your thoughts.
Usually, when you want more granularity than 1 minute, you have to write a daemon.
I advise you to try, now it's not so hard as it was some years ago. Just start with a simple loop inside a CLI command:
while (true) {
doPeriodicStuff();
sleep(1);
}
One important thing: run the daemon via supervisord. You can take a look at articles about Laravel's queue listener setup, it uses the same approach (a daemon + supervisord). A config section can look like this:
[program:your_daemon]
command=php artisan your:command --env=your_environment
directory=/path/to/laravel
stdout_logfile=/path/to/laravel/app/storage/logs/your_command.log
redirect_stderr=true
autostart=true
autorestart=true
for per second you can add command to cron job
* * * * * /usr/local/php56/bin/php56 /home/hamshahr/domains/hamshahrapp.com/project/artisan taxis:beFreeTaxis 1>> /dev/null 2>&1
and in command :
<?php
namespace App\Console\Commands;
use App\Contracts\Repositories\TaxiRepository;
use App\Contracts\Repositories\TravelRepository;
use App\Contracts\Repositories\TravelsRequestsDriversRepository;
use Carbon\Carbon;
use Illuminate\Console\Command;
class beFreeRequest extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'taxis:beFreeRequest';
/**
* The console command description.
*
* #var string
*/
protected $description = 'change is active to 0 after 1 min if yet is 1';
/**
* #var TravelsRequestsDriversRepository
*/
private $travelsRequestsDriversRepository;
/**
* Create a new command instance.
* #param TravelsRequestsDriversRepository $travelsRequestsDriversRepository
*/
public function __construct(
TravelsRequestsDriversRepository $travelsRequestsDriversRepository
)
{
parent::__construct();
$this->travelsRequestsDriversRepository = $travelsRequestsDriversRepository;
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$count = 0;
while ($count < 59) {
$startTime = Carbon::now();
$this->travelsRequestsDriversRepository->beFreeRequestAfterTime();
$endTime = Carbon::now();
$totalDuration = $endTime->diffInSeconds($startTime);
if($totalDuration > 0) {
$count += $totalDuration;
}
else {
$count++;
}
sleep(1);
}
}
}
$schedule->call(function(){
while (some-condition) {
runProcess();
}
})->name("someName")->withoutOverlapping();
Depending on how long your runProcess() takes to execute, you can use sleep(seconds) to have more fine tuning.
some-condition is normally a flag that you can change any time to have control on the infinite loop. e.g. you can use file_exists(path-to-flag-file) to manually start or stop the process any time you need.
this might be a bit old question but i recommend you to take a look at
laravel-short-schedule package from spatie
it allows you to run a commands at sub-minute or even sub-second
You can try and duplicate the jobs every second * 60 times using sleep(1).

Symfony run code after response was sent

I took a look at this other question. I am looking for a way to do what the OP of that question wants as well, and that is to continue processing php after sending http response, but in Symfony2.
I implemented an event that fires after every kernel termination. So far so good, but what I want is for it to fire after CERTAIN terminations, in specific controller actions, for instance after a form was sent, not every single time at every request. That is because I want to do some heavy tasks at certain times and don't want the end user to wait for the page to load.
Any idea how can I do that?
<?php
namespace MedAppBundle\Event;
use JMS\DiExtraBundle\Annotation\InjectParams;
use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\Tag;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use JMS\DiExtraBundle\Annotation\Inject;
/**
* Class MedicListener
* #package MedAppBundle\EventListener
* #Service("medapp_test.listener")
* #Tag(name="kernel.event_subscriber")
*/
class TestListener implements EventSubscriberInterface
{
private $container;
private $logger;
/**
* Constructor.
*
* #param ContainerInterface $container A ContainerInterface instance
* #param LoggerInterface $logger A LoggerInterface instance
* #InjectParams({
* "container" = #Inject("service_container"),
* "logger" = #Inject("logger")
* })
*/
public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
{
$this->container = $container;
$this->logger = $logger;
}
public function onTerminate()
{
$this->logger->notice('fired');
}
public static function getSubscribedEvents()
{
$listeners = array(KernelEvents::TERMINATE => 'onTerminate');
if (class_exists('Symfony\Component\Console\ConsoleEvents')) {
$listeners[ConsoleEvents::TERMINATE] = 'onTerminate';
}
return $listeners;
}
}
So far I've subscribed the event to the kernel.terminate one, but obviously this fires it at each request. I made it similar to Swiftmailer's EmailSenderListener
It feels kind of strange that the kernel must listen each time for this event even when it's not triggered. I'd rather have it fired only when needed, but not sure how to do that.
In the onTerminate callback you get an instance of PostResponseEvent as first parameter. You can get the Request as well as the Response from that object.
Then you should be able to decide if you want to run the actual termination code.
Also you can store custom data in the attributes bag of the Request. See this link: Symfony and HTTP Fundamentals
The Request class also has a public attributes property, which holds special data related to how the application works internally. For the Symfony Framework, the attributes holds the values returned by the matched route, like _controller, id (if you have an {id} wildcard), and even the name of the matched route (_route). The attributes property exists entirely to be a place where you can prepare and store context-specific information about the request.
Your code could look something like this:
// ...
class TestListener implements EventSubscriberInterface
{
// ...
public function onTerminate(PostResponseEvent $event)
{
$request = $event->getRequest();
if ($request->attributes->get('_route') == 'some_route_name') {
// do stuff
}
}
// ...
}
Edit:
The kernel.terminate event is designed to run after the response is sent. But the symfony documentation is saying the following (taken from here):
Internally, the HttpKernel makes use of the fastcgi_finish_request PHP function. This means that at the moment, only the PHP FPM server API is able to send a response to the client while the server's PHP process still performs some tasks. With all other server APIs, listeners to kernel.terminate are still executed, but the response is not sent to the client until they are all completed.
Edit 2:
To use the solution from here, you could either directly edit the web/app.php file to add it there (but this is some kind of "hacking core" imo, even though it would be easier to use than the following). Or you could do it like this:
Add a listener to kernel.request event with a high priority and start output buffering (ob_start).
Add a listener to kernel.response and add the header values to the response.
Add another listener with highest priority to kernel.terminate and do the flushing (ob_flush, flush).
Run your code in a separate listener with lower priority to kernel.terminate
I did not try it, but it should actually work.
To solve this issue for some of my use cases I simply create symfony commands to do the heavy tasks, and call them via exec() to make them run in a separate process.
I used these answers to write a Response class that has this functionality:
https://stackoverflow.com/a/28738208/1153227
This implementation will work on Apache and not just PHP FPM. However, to make this work we must prevent Apache from using gzip (by using an invalid Content-Encoding) so it makes sense to have a custom Response class to specify exactly when having an early response is more important than compression.
use Symfony\Component\HttpFoundation\Response;
class EarlyResponse extends Response
{
// Functionality adapted from this answer: https://stackoverflow.com/a/7120170/1153227
protected $callback = null;
/**
* Constructor.
*
* #param mixed $content The response content, see setContent()
* #param int $status The response status code
* #param array $headers An array of response headers
*
* #throws \InvalidArgumentException When the HTTP status code is not valid
*/
public function __construct($content = '', $status = 200, $headers = array(), $callback = null)
{
if (null !== $callback) {
$this->setTerminateCallback($callback);
}
parent::__construct($content, $status, $headers);
}
/**
* Sets the PHP callback associated with this Response.
* It will be called after the terminate events fire and thus after we've sent our response and closed the connection
*
* #param callable $callback A valid PHP callback
*
* #throws \LogicException
*/
public function setTerminateCallback($callback)
{
//Copied From Symfony\Component\HttpFoundation\StreamedResponse
if (!is_callable($callback)) {
throw new \LogicException('The Response callback must be a valid PHP callable.');
}
$this->callback = $callback;
}
/**
* #return Current_Class_Name
*/
public function send() {
if (function_exists('fastcgi_finish_request') || 'cli' === PHP_SAPI) { // we don't need the hack when using fast CGI
return parent::send();
}
ignore_user_abort(true);//prevent apache killing the process
if (!ob_get_level()) { // Check if an ob buffer exists already.
ob_start();//start the output buffer
}
$this->sendContent(); //Send the content to the buffer
static::closeOutputBuffers(1, true); //flush all but the last ob buffer level
$this->headers->set('Content-Length', ob_get_length()); // Set the content length using the last ob buffer level
$this->headers->set('Connection', 'close'); // Close the Connection
$this->headers->set('Content-Encoding', 'none');// This invalid header value will make Apache not delay sending the response while it is
// See: https://serverfault.com/questions/844526/apache-2-4-7-ignores-response-header-content-encoding-identity-instead-respect
$this->sendHeaders(); //Now that we have the headers, we can send them (which will avoid the ob buffers)
static::closeOutputBuffers(0, true); //flush the last ob buffer level
flush(); // After we flush the OB buffer to the normal buffer, we still need to send the normal buffer to output
session_write_close();//close session file on server side to avoid blocking other requests
return $this;
}
/**
* #return Current_Class_Name
*/
public function callTerminateCallback() {
if ($this->callback) {
call_user_func($this->callback);
}
return $this;
}
}
You also need to add a method to your AppKernel.php to make this work (don't forget to add a use statement for your EarlyResponse class)
public function terminate(Request $request, Response $response)
{
ob_start();
//Run this stuff before the terminate events
if ($response instanceof EarlyResponse) {
$response->callTerminateCallback();
}
//Trigger the terminate events
parent::terminate($request, $response);
//Optionally, we can output the beffer that will get cleaned to a file before discarding its contents
//file_put_contents('/tmp/process.log', ob_get_contents());
ob_end_clean();
}

How to get the Mink Selenium 2 Driver to wait for the page to load with Behat

I am in the process of upgrading from the Behat 2.x series to the Behat 3.x series. In the prior version I could load the Selenium 1 driver, which attached to PhantomJS to execute tests. When I did this I was able to hook into a function called waitForPageToLoad().
This function was provided by php-selenium (from Alexandre Salomé). It hooked into selenium and called a driver action by the same name. This worked perfectly for ensuring that Selenium waited for a page to load. At least until a timeout was reached. It made tests to go much faster.
The problem is that the Selenium 1 driver is not compatible with Behat 3.x. It looks like it has been all but abandoned and I don't see that functionality in the Selenium 2 driver for Mink.
Does anyone know of a way to make this work with Behat 3.x and Selenium 2?
Using Selenium (or any other driver for that matter), I've never had to worry about whether the page has loaded or not, with one exception: if the page finishes loading, then loads more content via AJAX.
To handle this, you can use a spin function as documented in the Behat Manual.
http://docs.behat.org/en/v2.5/cookbook/using_spin_functions.html
The benefits of this are:
It doesn't need you to use the selenium driver (for example, you could use PhantomJS if you want speed over looks).
It won't break if you stop using jQuery and switch to something else (such as Angular's $httpProvider)
I wouldn't use theirs though, the back trace is broken and who want's to wait a second between checks anyway. :)
Try this:
Assuming you are using the Mink Context (thanks Mick), you can simply check the page every second or so until the desired
text has either appeared or dissapeared, or a given timeout has expired in which case we'd assume a fail.
/**
* #When I wait for :text to appear
* #Then I should see :text appear
* #param $text
* #throws \Exception
*/
public function iWaitForTextToAppear($text)
{
$this->spin(function(FeatureContext $context) use ($text) {
try {
$context->assertPageContainsText($text);
return true;
}
catch(ResponseTextException $e) {
// NOOP
}
return false;
});
}
/**
* #When I wait for :text to disappear
* #Then I should see :text disappear
* #param $text
* #throws \Exception
*/
public function iWaitForTextToDisappear($text)
{
$this->spin(function(FeatureContext $context) use ($text) {
try {
$context->assertPageContainsText($text);
}
catch(ResponseTextException $e) {
return true;
}
return false;
});
}
/**
* Based on Behat's own example
* #see http://docs.behat.org/en/v2.5/cookbook/using_spin_functions.html#adding-a-timeout
* #param $lambda
* #param int $wait
* #throws \Exception
*/
public function spin($lambda, $wait = 60)
{
$time = time();
$stopTime = $time + $wait;
while (time() < $stopTime)
{
try {
if ($lambda($this)) {
return;
}
} catch (\Exception $e) {
// do nothing
}
usleep(250000);
}
throw new \Exception("Spin function timed out after {$wait} seconds");
}
Selenium2 now has the wait($timeout, $condition) function.
You can use it like:
/**
* #Then /^I wait for the ajax response$/
*/
public function iWaitForTheAjaxResponse()
{
$this->getSession()->wait(5000, '(0 === jQuery.active)');
}
Other conditions that you could test for are:
the appearance of a certain element on the page
the DOM to finish loading
The reason for the change is outlined on the selenium website documentation
In order to help someone else, I added this method in FeatureContext.php :
/**
* #Then I wait :sec
*/
public function wait($sec)
{
sleep($sec);
}
And it's working
Will
/**
* #Then /^I wait for the ajax response$/
*/
public function iWaitForTheAjaxResponse()
{
$this->getSession()->wait(5000, '(0 === jQuery.active)');
}
it's working

How to listen to a TCP port using PHP? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have a GPS Tracker that connects and send data to a defined public server:port through GPRS connection.
I can define the ip:port of the GPS device
My question is, can I just open a port in my server and listen/save the data received using PHP?
Thanks.
Edit/Update Aug. 16, 2017 :
User and library author <#Navarr> has commented that he has released a new, updated version of the library the code in my original answer was based from. A link to the new code on his github here. Feel free to explore the new code and refer back to the original example here for insight (I have no personally explored/used the new code).
The below code will use the SocketServer.class.php file found here. It is meant to be run as a standalone process which means under Linux I had to make the file executable, then run it from command line using "php my_server.php".
For more information on running php scripts from command line:
http://www.funphp.com/?p=33
First grab the SocketServer.class.php file here:
http://www.phpclasses.org/browse/file/31975.html
Try this to make use of it, then tweak it to handle receiving your own incoming data as needed. Hope it helps.
<?php
require_once("SocketServer.class.php"); // Include the File
$server = new SocketServer("192.168.1.6",31337); // Create a Server binding to the given ip address and listen to port 31337 for connections
$server->max_clients = 10; // Allow no more than 10 people to connect at a time
$server->hook("CONNECT","handle_connect"); // Run handle_connect every time someone connects
$server->hook("INPUT","handle_input"); // Run handle_input whenever text is sent to the server
$server->infinite_loop(); // Run Server Code Until Process is terminated.
function handle_connect(&$server,&$client,$input)
{
SocketServer::socket_write_smart($client->socket,"String? ","");
}
function handle_input(&$server,&$client,$input)
{
// You probably want to sanitize your inputs here
$trim = trim($input); // Trim the input, Remove Line Endings and Extra Whitespace.
if(strtolower($trim) == "quit") // User Wants to quit the server
{
SocketServer::socket_write_smart($client->socket,"Oh... Goodbye..."); // Give the user a sad goodbye message, meany!
$server->disconnect($client->server_clients_index); // Disconnect this client.
return; // Ends the function
}
$output = strrev($trim); // Reverse the String
SocketServer::socket_write_smart($client->socket,$output); // Send the Client back the String
SocketServer::socket_write_smart($client->socket,"String? ",""); // Request Another String
}
Edit: In keeping things relevant and functional for this answer I found it best not to continue to rely on code from an external source that may not always remain available (or at the given URL provided in my link). Therefore, for convenience, I am adding below the code that corresponds to the SocketServer.class.php file I linked to at the top of this post. (Apologies for length and possible lack of indentation/formatting while copy/pasting, I am not the author of the code below).
<?php
/*! #class SocketServer
#author Navarr Barnier
#abstract A Framework for creating a multi-client server using the PHP language.
*/
class SocketServer
{
/*! #var config
#abstract Array - an array of configuration information used by the server.
*/
protected $config;
/*! #var hooks
#abstract Array - a dictionary of hooks and the callbacks attached to them.
*/
protected $hooks;
/*! #var master_socket
#abstract resource - The master socket used by the server.
*/
protected $master_socket;
/*! #var max_clients
#abstract unsigned int - The maximum number of clients allowed to connect.
*/
public $max_clients = 10;
/*! #var max_read
#abstract unsigned int - The maximum number of bytes to read from a socket at a single time.
*/
public $max_read = 1024;
/*! #var clients
#abstract Array - an array of connected clients.
*/
public $clients;
/*! #function __construct
#abstract Creates the socket and starts listening to it.
#param string - IP Address to bind to, NULL for default.
#param int - Port to bind to
#result void
*/
public function __construct($bind_ip,$port)
{
set_time_limit(0);
$this->hooks = array();
$this->config["ip"] = $bind_ip;
$this->config["port"] = $port;
$this->master_socket = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($this->master_socket,$this->config["ip"],$this->config["port"]) or die("Issue Binding");
socket_getsockname($this->master_socket,$bind_ip,$port);
socket_listen($this->master_socket);
SocketServer::debug("Listenting for connections on {$bind_ip}:{$port}");
}
/*! #function hook
#abstract Adds a function to be called whenever a certain action happens. Can be extended in your implementation.
#param string - Command
#param callback- Function to Call.
#see unhook
#see trigger_hooks
#result void
*/
public function hook($command,$function)
{
$command = strtoupper($command);
if(!isset($this->hooks[$command])) { $this->hooks[$command] = array(); }
$k = array_search($function,$this->hooks[$command]);
if($k === FALSE)
{
$this->hooks[$command][] = $function;
}
}
/*! #function unhook
#abstract Deletes a function from the call list for a certain action. Can be extended in your implementation.
#param string - Command
#param callback- Function to Delete from Call List
#see hook
#see trigger_hooks
#result void
*/
public function unhook($command = NULL,$function)
{
$command = strtoupper($command);
if($command !== NULL)
{
$k = array_search($function,$this->hooks[$command]);
if($k !== FALSE)
{
unset($this->hooks[$command][$k]);
}
} else {
$k = array_search($this->user_funcs,$function);
if($k !== FALSE)
{
unset($this->user_funcs[$k]);
}
}
}
/*! #function loop_once
#abstract Runs the class's actions once.
#discussion Should only be used if you want to run additional checks during server operation. Otherwise, use infinite_loop()
#param void
#see infinite_loop
#result bool - True
*/
public function loop_once()
{
// Setup Clients Listen Socket For Reading
$read[0] = $this->master_socket;
for($i = 0; $i < $this->max_clients; $i++)
{
if(isset($this->clients[$i]))
{
$read[$i + 1] = $this->clients[$i]->socket;
}
}
// Set up a blocking call to socket_select
if(socket_select($read,$write = NULL, $except = NULL, $tv_sec = 5) < 1)
{
// SocketServer::debug("Problem blocking socket_select?");
return true;
}
// Handle new Connections
if(in_array($this->master_socket, $read))
{
for($i = 0; $i < $this->max_clients; $i++)
{
if(empty($this->clients[$i]))
{
$temp_sock = $this->master_socket;
$this->clients[$i] = new SocketServerClient($this->master_socket,$i);
$this->trigger_hooks("CONNECT",$this->clients[$i],"");
break;
}
elseif($i == ($this->max_clients-1))
{
SocketServer::debug("Too many clients... :( ");
}
}
}
// Handle Input
for($i = 0; $i < $this->max_clients; $i++) // for each client
{
if(isset($this->clients[$i]))
{
if(in_array($this->clients[$i]->socket, $read))
{
$input = socket_read($this->clients[$i]->socket, $this->max_read);
if($input == null)
{
$this->disconnect($i);
}
else
{
SocketServer::debug("{$i}#{$this->clients[$i]->ip} --> {$input}");
$this->trigger_hooks("INPUT",$this->clients[$i],$input);
}
}
}
}
return true;
}
/*! #function disconnect
#abstract Disconnects a client from the server.
#param int - Index of the client to disconnect.
#param string - Message to send to the hooks
#result void
*/
public function disconnect($client_index,$message = "")
{
$i = $client_index;
SocketServer::debug("Client {$i} from {$this->clients[$i]->ip} Disconnecting");
$this->trigger_hooks("DISCONNECT",$this->clients[$i],$message);
$this->clients[$i]->destroy();
unset($this->clients[$i]);
}
/*! #function trigger_hooks
#abstract Triggers Hooks for a certain command.
#param string - Command who's hooks you want to trigger.
#param object - The client who activated this command.
#param string - The input from the client, or a message to be sent to the hooks.
#result void
*/
public function trigger_hooks($command,&$client,$input)
{
if(isset($this->hooks[$command]))
{
foreach($this->hooks[$command] as $function)
{
SocketServer::debug("Triggering Hook '{$function}' for '{$command}'");
$continue = call_user_func($function,$this,$client,$input);
if($continue === FALSE) { break; }
}
}
}
/*! #function infinite_loop
#abstract Runs the server code until the server is shut down.
#see loop_once
#param void
#result void
*/
public function infinite_loop()
{
$test = true;
do
{
$test = $this->loop_once();
}
while($test);
}
/*! #function debug
#static
#abstract Outputs Text directly.
#discussion Yeah, should probably make a way to turn this off.
#param string - Text to Output
#result void
*/
public static function debug($text)
{
echo("{$text}\r\n");
}
/*! #function socket_write_smart
#static
#abstract Writes data to the socket, including the length of the data, and ends it with a CRLF unless specified.
#discussion It is perfectly valid for socket_write_smart to return zero which means no bytes have been written. Be sure to use the === operator to check for FALSE in case of an error.
#param resource- Socket Instance
#param string - Data to write to the socket.
#param string - Data to end the line with. Specify a "" if you don't want a line end sent.
#result mixed - Returns the number of bytes successfully written to the socket or FALSE on failure. The error code can be retrieved with socket_last_error(). This code may be passed to socket_strerror() to get a textual explanation of the error.
*/
public static function socket_write_smart(&$sock,$string,$crlf = "\r\n")
{
SocketServer::debug("<-- {$string}");
if($crlf) { $string = "{$string}{$crlf}"; }
return socket_write($sock,$string,strlen($string));
}
/*! #function __get
#abstract Magic Method used for allowing the reading of protected variables.
#discussion You never need to use this method, simply calling $server->variable works because of this method's existence.
#param string - Variable to retrieve
#result mixed - Returns the reference to the variable called.
*/
function &__get($name)
{
return $this->{$name};
}
}
/*! #class SocketServerClient
#author Navarr Barnier
#abstract A Client Instance for use with SocketServer
*/
class SocketServerClient
{
/*! #var socket
#abstract resource - The client's socket resource, for sending and receiving data with.
*/
protected $socket;
/*! #var ip
#abstract string - The client's IP address, as seen by the server.
*/
protected $ip;
/*! #var hostname
#abstract string - The client's hostname, as seen by the server.
#discussion This variable is only set after calling lookup_hostname, as hostname lookups can take up a decent amount of time.
#see lookup_hostname
*/
protected $hostname;
/*! #var server_clients_index
#abstract int - The index of this client in the SocketServer's client array.
*/
protected $server_clients_index;
/*! #function __construct
#param resource- The resource of the socket the client is connecting by, generally the master socket.
#param int - The Index in the Server's client array.
#result void
*/
public function __construct(&$socket,$i)
{
$this->server_clients_index = $i;
$this->socket = socket_accept($socket) or die("Failed to Accept");
SocketServer::debug("New Client Connected");
socket_getpeername($this->socket,$ip);
$this->ip = $ip;
}
/*! #function lookup_hostname
#abstract Searches for the user's hostname and stores the result to hostname.
#see hostname
#param void
#result string - The hostname on success or the IP address on failure.
*/
public function lookup_hostname()
{
$this->hostname = gethostbyaddr($this->ip);
return $this->hostname;
}
/*! #function destroy
#abstract Closes the socket. Thats pretty much it.
#param void
#result void
*/
public function destroy()
{
socket_close($this->socket);
}
function &__get($name)
{
return $this->{$name};
}
function __isset($name)
{
return isset($this->{$name});
}
}
Yep, using socket_create, socket_bind, socket_listen, and socket_accept
http://www.php.net/manual/en/function.socket-create.php
http://www.php.net/manual/en/function.socket-bind.php
http://www.php.net/manual/en/function.socket-listen.php
http://www.php.net/manual/en/function.socket-accept.php
There are lots of examples on those pages on how to use them.
Yes, PHP offers a socket_listen() function just for that. http://php.net/manual/en/function.socket-listen.php.

Categories