I'm trying to use zookeeper in a php app, and I've done most of the get($path)/set($path, $value)/getChildren($path) functions following https://github.com/andreiz/php-zookeeper, except the watch_callback function that just doesn't work.
My php version is 5.6.14 and thread safety disabled, and I'm using apache2.4.
Here is some code snippet
class Zookeeper_Module {
private $zookeeper;
public function __construct(){
$this->ci = & get_instance();
$zookeeper_server = $this->ci->config->item('zookeeper_server');
$this->zookeeper = new Zookeeper($zookeeper_server);
}
public function set($path, $value){
$this->zookeeper->set($path, $value);
}
public function get($path, $watch_cb = null){
return $this->zookeeper->get($path, $watch_cb);
}
public function get_watch_cb($event_type = '', $stat = '', $path = ''){
error_log('hello from get_watcher_cb');
$value = $this->get($path, array($this, 'get_watch_cb'));
// update redis cache
$this->ci->cache->redis->save('some cache key', $value);
}
}
class MyTest{
public function get(){
$zookeeper = new Zookeeper_Module ();
$value = $zookeeper->get( '/foo/bar', array (
$zookeeper,
'get_watch_cb'
) );
}
public function set(){
$zookeeper = new Zookeeper_Module ();
$zookeeper->set( '/foo/bar', 'some value');
}
}
I can successfully get or set a node value, but I can neither catch watch callback log nor have redis cache updated.
I write a more simple demo, very similar with this https://github.com/andreiz/php-zookeeper/wiki, and the watcher works fine in this demo.
The most significant difference is
while( true ) {
echo '.';
sleep(2);
}
While java has a jvm container hosting watchers, php doesn't has a container to do it, so we have to use a while(true) to keep watchers alive.
So I add a while(true) in my code and now the watcher works fine.
But I don't want to add a terrible while(true) in a web app, so the final solution is adding a java app to communicate with zookeeper and save results in redis, and php app just reads info from redis.
Related
I have the following scenario:
I have an API built with the Slim PHP framework. I am using the PHP lib Ratchet to run a WebSocket server. Once the WebSocket server is started, I want to run a function that does some computation while the server is running.
So far, inside my API, I have a route that calls the MyMethod method of a class MyClass. Inside the class, I have the following:
class MyClass {
public $calculation_status;
public function MyMethod() {
$server = IoServer::factory(
new HttpServer(
new WsServer(
new messengerApp($this)
)
),
8080
);
$this->doCalculationAsynchronously()->then(
function ($result) {
$this->calculation_status = 'finished';
},
function ($reason) {
$this->calculation_status = 'stopped';
},
function ($update) {
$this->calculation_status = 'still working...';
}
}
$server->run($this);
}
public function doCalculationAsynchronously() {
$deferred = new Deferred();
$this->computeSomethingAsync(function ($error = null, $result) use ($deferred) {
if ($error) {
$deferred->reject($error);
} else {
$deferred->resolve($result);
}
});
return $deferred->promise();
}
public function computeSomethingAsync() {
// Simulate a long running calculation
while(true){} // OR sleep(1000000);
return $result;
}
}
So, I'm expecting this to try to start running the asynchronous calculation function, return a promise to MyMethod, and then run my WebSocket server.
The reason for injecting $this into the server is to access my calculation_status property and be able to send it to clients through the WS.
This code is inspired by the example for Deferred in the ReactPHP doc
Note: If I don't have the forever while loop, it goes on and runs the server correctly (but this is synchronous behavior; my goal for the server is to send the calculation status to clients). Injecting the class into the object works fine as well.
I am new to PHP generally and caching and am trying to develop a Facebook share counter for Wordpress. As advised by a member here, since it's not optimum to make calls to the API and slow down the website each time, I decided to cache the results and went with the static method. Here's the code I am using.
function fb_cache($atts) {
$url = $atts['url'];
static $fb_cache = array();
if (isset($fb_cache[$url])) {
$fb_count = $fb_cache[$url];
return $fb_count;
} else {
$fb = json_decode( file_get_contents('http://graph.facebook.com/' . $url) );
$fb_count = $fb->share->share_count;
$fb_cache[$url] = $fb_count;
return $fb_count;
}
}
This doesn't seem to work as the number keeps changing every few seconds and the API calls are thus being made each time. To use it as a plugin, I also have an instantiating code in the end.
static function get_instance() {
static $instance = false;
if ( ! $instance ) {
$instance = new self;
}
}
Can someone tell me what I am doing wrong. I apologize if it's a noob question and if I am using the wrong method altogether.
I'm trying to run a job queue to create a PDF file using SlmQueueBeanstalkd and DOMPDFModule in ZF".
Here's what I'm doing in my controller:
public function reporteAction()
{
$job = new TareaReporte();
$queueManager = $this->serviceLocator->get('SlmQueue\Queue\QueuePluginManager');
$queue = $queueManager->get('myQueue');
$queue->push($job);
...
}
This is the job:
namespace Application\Job;
use SlmQueue\Job\AbstractJob;
use SlmQueue\Queue\QueueAwareInterface;
use SlmQueue\Queue\QueueInterface;
use DOMPDFModule\View\Model\PdfModel;
class TareaReporte extends AbstractJob implements QueueAwareInterface
{
protected $queue;
public function getQueue()
{
return $this->queue;
}
public function setQueue(QueueInterface $queue)
{
$this->queue = $queue;
}
public function execute()
{
$sm = $this->getQueue()->getJobPluginManager()->getServiceLocator();
$empresaTable = $sm->get('Application\Model\EmpresaTable');
$registros = $empresaTable->listadoCompleto();
$model = new PdfModel(array('registros' => $registros));
$model->setOption('paperSize', 'letter');
$model->setOption('paperOrientation', 'portrait');
$model->setTemplate('empresa/reporte-pdf');
$output = $sm->get('viewPdfrenderer')->render($model);
$filename = "/path/to/pdf/file.pdf";
file_put_contents($filename, $output);
}
}
The first time you run it, the file is created and the work is successful, however, if you run a second time, the task is buried and the file is not created.
It seems that stays in an endless cycle when trying to render the model a second time.
I've had a similar issue and it turned out it was because of the way ZendPdf\PdfDocument reuses it's object factory. Are you using ZendPdf\PdfDocument?
You might need to correctly close factory.
class MyDocument extends PdfDocument
{
public function __destruct()
{
$this->_objFactory->close();
}
}
Try to add this or something similar to the PdfDocument class...
update : it seem you are not using PdfDocument, however I suspect this is the issue is the same. Are you able to regenerate a second PDF in a normal http request? It is your job to make sure the environment is equal on each run.
If you are unable to overcome this problem a short-term quick solution would be to set max_runs configuration for SlmQueue to 1. That way the worker is stopped after each job and this reset to a vanilla state...
So i have the following code:
private function getArtistInfo($artist){
$artisan = json_decode($artist, true);
$artistObj = array();
//fb($artist);
$artistObj['id'] = $artisan['name']['ids']['nameId'];
$memcache = new Memcached($artistObj['id']);
$artistCache = $memcache->getMemcache();
if($artistCache === false){
$artistObj['name'] = $artisan['name']['name'];
$artistObj['image'] = $artisan['name']['images'][0]['url'];
$initArtist = array('id' => $artistObj['id'], 'name' => $artistObj['name'], 'image' => $artistObj['image']);
$artistObj = $this->buildArtist($artisan, $artistObj);
$memcache->setMemcache($artistObj);
}
else{
$initArtist = array('id' => $artistCache['id'], 'name' => $artistCache['name'], 'image' => $artistCache['image']);
}
return $initArtist;
}
Now the code works but it takes getArtistInfo() too long to finish when i just want the $initArtist value; I would like my client to get right away the $initArtist once its constructed, and somehow let the caching of $artistObj runs in the background.
So far i have read up on several different topic i thought might be useful: event delegation, callback function, call_user_func, observer pattern, threading, gearman etc. However, I have no idea which one of them would actually do what i want. Please point me to the right direction.
EDIT:
My Memcached class:
class Memcached {
private static $MEMCACHED_HOST = "localhost";
private static $MEMCACHED_PORT = "11211";
private $id, $key, $memcache, $cacheOK;
function __construct ($id){
$this->id = $id;
$this->key = 'artistID_'. $this->id;
$this->memcache = new Memcache;
$this->cacheOK = $this->memcache->connect(Memcached::$MEMCACHED_HOST, Memcached::$MEMCACHED_PORT);
}
protected function getMemcache(){
$artistInfo = null;
if($this->cacheOK === true){
$artistInfo = $this->memcache->get($this->key);
}
if($artistInfo === false){
return false;
}
return $artistInfo;
}
public function setMemcache($artistInfo){
$this->memcache->set($this->key, $artistInfo, 0, 60);
}
}
My buildArtist() code:
private function buildArtist($artisan, $artistObj){
$artistObj['amgID'] = $artisan['name']['ids']['amgPopId'];
$discography = $artisan['name']['discography'];
foreach($discography as $album){
$albumID = $album['ids']['amgPopId'];
preg_match('/(\d+)/', $albumID, $matches);
$albumObj['amgAlbumID'] = $matches[1];
$albumObj['title'] = $album['title'];
$albumObj['releaseDate'] = $album['year'];
$albumObj['more'] = $this->getMoreMusic($albumObj['title'], $artistObj['name']);
$artistObj['discography'][] = $albumObj;
}
return $artistObj;
}
Well, it's not entirely clearly how long too long is, or which part of this code is what's slowing you down. For all we know, the slow part isn't the part that stores the data in Memcached.
In any case, once you do identify that this is your bottleneck, one thing you can do to accomplish this type of out of order execution is use a brokerless messaging queue like ZeroMQ to accept JSON object that need cached. A separate PHP script can then take on the job of processing and caching these requests asynchronously outside of any web-request. This separate script could be run through a cron-job or some other job manager that handles the caching part in parallel.
You want to use set and get rather than using the memcache persistence ID, i'm not even sure what setMemcache and getMemcache are but they aren't in the extension documentation.
Here's an example from the documentation:
<?php
$m = new Memcached();
$m->addServer('localhost', 11211);
if (!($ip = $m->get('ip_block'))) {
if ($m->getResultCode() == Memcached::RES_NOTFOUND) {
$ip = array();
$m->set('ip_block', $ip);
} else {
/* log error */
/* ... */
}
}
Please show the code of buildArtist for help on optimizing it.
I have an interesting problem and have searched the internet, but haven't yet found an answer.
I work for a company that doesn't allow it's workers to utilize OOP, it is kind of ridiculous, but the working experience is valuable.
Consider the following function:
function get_setting_values_from_file( $parameter )
{
exec("/usr/var/binary --options $parameter", $output, $return);
$settings = file( $output[0] );
foreach( $settings as $setting ) {
if( strstr( $setting, "color") ) {
$setting = explode( ":", $setting );
return $setting[1];
}
}
return false;
}
I need to unit test a similar function. I am currently using phpUnit for my tests and the vfsStream libraries to mock the file system, but how do you mock the call to exec("/usr/var/binary --options $parameter", $output, $return) when I'm developing with no access to the actual system? What is the recommend approach for dealing with test cases like this?
All feedback is appreciated.
You could mock exec() by using a function mock library. I made one (php-mock) for you which requires you to use namespaces
namespace foo;
use phpmock\phpunit\PHPMock;
class ExecTest extends \PHPUnit_Framework_TestCase
{
use PHPMock;
public function testExec()
{
$mock = $this->getFunctionMock(__NAMESPACE__, "exec");
$mock->expects($this->once())->willReturnCallback(
function ($command, &$output, &$return_var) {
$this->assertEquals("foo", $command);
$output = "failure";
$return_var = 1;
}
);
exec("foo", $output, $return_var);
$this->assertEquals("failure", $output);
$this->assertEquals(1, $return_var);
}
}
Simply mock this function to return the text that you are trying to get into $settings. You do not need to call the executable, simply create the file or return.
For instance, assuming the function get_setting_values_from_file() returns the settings as an array, you can simply mock the function in your test to return the settings as an array. Create a test stub to mock the object that contains the get_setting_values_from_file() method, and have that mock simply return the same FALSE, 1 or 2 that the test assumed.
$stub = $this->getMock('GetSettingsClass');
$stub->expects($this->any())
->method('get_settings_from_file')
->will($this->returnValue(0));
This is from the PHPUnit manual -> http://phpunit.de/manual/3.8/en/test-doubles.html#test-doubles.stubs
Optionally, you could even bypass the call, and simply test the functions/code that works on the returns by creating the array and passing it to those functions.
Assumed Example in the main code:
...
$settings = get_setting_values_from_file( 'UserType' );
$UserType = get_user_type($settings);
return $UserType;
function get_user_type($settings)
{
if($settings !== FALSE) // Returned from your function if parameter is not found
{
switch($settings)
{
case 1:
return 'User'; // Best to use Constants, but for example here only
break;
case 2:
return 'Admin';
break;
...
}
}
else
{
return FALSE;
}
}
Now, in your test, you can simply
$this->assertFalse(get_user_type(FALSE, 'Ensure not found data is handled properly as FALSE is returned');
$this->assertEqual('User', get_user_type(1), 'Test UserType=1');
$this->assertEqual('Admin', get_user_type(1), 'Test UserType=2');
...
These work as the code does not call the function that had to mock the read from the OS, but does handle all the expected returns by calling the function processing the setting return value. Here, you have simply assumed the return from the function 'get_setting_values_from_file()' without needing the file or any mocks.
This does NOT however test reading from the file, which I would do in another test by using the setUp and tearDown to actual create a file with the values you want (fopen/fwrite) and then call your function and ensure it returns what is expected.
I hope this helps to explain what I was thinking.