Zend Framework Cron for FIFO Queue - php

I am trying to run a cron job in my application my set up is like this:
My zend application Version 1.12
inside my public/index.php
function Mylib_init_settings($settings, $environment)
{
if (getenv('LOCAL_ENV') && file_exists($serverConfigFile = __DIR__ . '/../application/configs/' . getenv('LOCAL_ENV') . '.ini')) {
$settings->addOverrideFile($serverConfigFile);
}
}
define('MYLIB_APPLICATION_ENV', 'production');
require __DIR__ . '/../library/Mylib/Application/start.php';
Inside Start.php
<?php
use Mylib\Config;
use Mylib\Config\Loader\SecondGeneration;
function mylib_trigger_hook($hook, $params = array())
{
$func = 'mylib_init_' . strtolower(trim($hook));
if (function_exists($func)) {
call_user_func_array($func, $params);
}
}
// ---------------------------------------------------------------------------------------------------------------------
// setup application constants
if (getenv('SELENIUM')) {
defined('MYLIB_APPLICATION_ENV')
?: define('MYLIB_APPLICATION_ENV', 'testing');
}
// should the application be bootstrapped?
defined('MYLIB_APPLICATION_BOOTSTRAP')
?: define('MYLIB_APPLICATION_BOOTSTRAP', true);
// should the application run?
defined('MYLIB_APPLICATION_CREATE')
?: define('MYLIB_APPLICATION_CREATE', true);
// should the application run?
defined('MYLIB_APPLICATION_RUN')
?: define('MYLIB_APPLICATION_RUN', true);
// maximum execution time
defined('MYLIB_APPLICATION_TIME_LIMIT')
?: define('MYLIB_APPLICATION_TIME_LIMIT', 0);
// path to application rooth
defined('MYLIB_APPLICATION_PATH_ROOT')
?: define('MYLIB_APPLICATION_PATH_ROOT', realpath(__DIR__ . '/../../../'));
// path to library
defined('MYLIB_APPLICATION_PATH_LIBRARY')
?: define('MYLIB_APPLICATION_PATH_LIBRARY', realpath(__DIR__ . '/../../'));
mylib_trigger_hook('constants');
// ---------------------------------------------------------------------------------------------------------------------
// limits the maximum execution time
set_time_limit(MYLIB_APPLICATION_TIME_LIMIT);
// ---------------------------------------------------------------------------------------------------------------------
// determine which configuration section, and overrides to load
$configSection = defined('MYLIB_APPLICATION_ENV') ?MYLIB_APPLICATION_ENV : null;
$configOverride = null;
$environmentFilename = MYLIB_APPLICATION_PATH_ROOT . '/environment';
if (file_exists($environmentFilename)) {
$ini = parse_ini_file($environmentFilename);
if ($ini === false) {
throw new \RuntimeException('Failed to parse enviroment file: ' . $environmentFilename);
}
if (!defined('MYLIB_APPLICATION_ENV')) {
// should have at least a config.section variable
if (!isset($ini['config.section'])) {
throw new \RuntimeException('\'config.section\' setting is missing in environment file');
}
$configSection = $ini['config.section'];
}
if (isset($ini['config.override'])) {
$configOverrideFilename = MYLIB_APPLICATION_PATH_ROOT . '/application/configs/' . $ini['config.override'] . '.ini';
if (!is_readable($configOverrideFilename)) {
throw new \RuntimeException(
sprintf('You have provided a config override file (%s), but it is not readable', $configOverrideFilename)
);
} else {
$configOverride = $configOverrideFilename;
}
}
}
defined('MYLIB_APPLICATION_ENV')
?: define('MYLIB_APPLICATION_ENV', $configSection);
static $allowedEnvironmnets = array(
'production',
'staging',
'testing',
'development',
);
if (!in_array(MYLIB_APPLICATION_ENV, $allowedEnvironmnets)) {
throw new \RuntimeException(
sprintf('Invalid environment %s provided. Must be either of: %s', MYLIB_APPLICATION_ENV, implode(', ', $allowedEnvironmnets))
);
}
macq_trigger_hook('environment', array(MYLIB_APPLICATION_ENV));
// ---------------------------------------------------------------------------------------------------------------------
// set the include path
set_include_path(MYLIB_APPLICATION_PATH_LIBRARY . PATH_SEPARATOR . get_include_path());
mylib_trigger_hook('includepath', array(get_include_path()));
// ---------------------------------------------------------------------------------------------------------------------
// enable PSR-0 autoloading
require_once MYLIB_APPLICATION_PATH_LIBRARY . '/Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()
->setFallbackAutoloader(true);
// ---------------------------------------------------------------------------------------------------------------------
// load configuration settings, and if an override is specified, merge it
$settings = new SecondGeneration(
MYLIB_APPLICATION_PATH_ROOT,
MYLIB_APPLICATION_ENV,
MYLIB_APPLICATION_PATH_LIBRARY . '/MyLib/Application/configuration.ini'
);
if ($configOverride) {
$settings->addOverrideFile($configOverride);
}
// set up config file caching, this is a seperate cache then any application caches created!
if (isset($ini['config.cache.enabled']) && $ini['config.cache.enabled']) {
if (isset($ini['config.cache.dir']) && is_writable($ini['config.cache.dir'])) {
$configCache = new Zend_Cache_Core(array('automatic_serialization'=>true));
$backend = new Zend_Cache_Backend_File(array(
'cache_dir' => $ini['config.cache.dir'],
));
$configCache->setBackend($backend);
$settings->setCache($configCache);
unset($configCache, $backend);
} else {
throw new \RuntimeException(
sprintf('Configuration cache is enabled, but no correct cache dir is specified, or the specified directory is not writable')
);
}
}
// load configuration settings
Config::load($settings);
mylib_trigger_hook('settings', array($settings, MYLIB_APPLICATION_ENV));
// ---------------------------------------------------------------------------------------------------------------------
// create application and bootstrap
if (MYLIB_APPLICATION_CREATE) {
$application = new Zend_Application(Config::environment(), Config::config());
macq_trigger_hook('application', array($application));
if (MYLIB_APPLICATION_BOOTSTRAP) {
$application->bootstrap();
macq_trigger_hook('bootstrap', array($application));
}
// ---------------------------------------------------------------------------------------------------------------------
// run application?
if (MYLIB_APPLICATION_RUN) {
$application->run();
macq_trigger_hook('run', array($application));
}
}
What I did is :
I followed the following link:
http://www.magentozend.com/blog/2012/02/03/setting-up-cronjobs-for-zend-framework-envoirment/
what I did is create a "cron" folder at the level in which my application folders are present.
inside the folder created init.php file inside that I added my index.php code and start.php code.
and my controller file is as like this:
application/modules/myproject/controller/cronjop.php
inside the cron job file I just called init.php
by
require_once('/../../../../cron/init.php');
but the cron is not working can some one help me..
thanks in advance..

I see you miss the point of using Cron and Zend as well. Zend site is just normal site so you can use for example lynx browser to run the site.
*/10 * * * * lynx -dump http://www.myzendsite.com/mycontroller/mycronaction
just create My Controller add mycron Action and put in this method what you want cron to do. Lynx will open it as normal user would do. Cron will run lynx after some time.
The line */10 means every 10 minutes. You can fit it to your needs.
There are other ways to run php script for example via php parser or curl.
Check this tutorial

Related

PHP Eval alternative to include a file

I am currently running a queue system with beanstalk + supervisor + PHP.
I would like my workers to automatically die when a new version is available (basically code update).
My current code is as follow
class Job1Controller extends Controller
{
public $currentVersion = 5;
public function actionIndex()
{
while (true) {
// check if a new version of the worker is available
$file = '/config/params.php';
$paramsContent = file_get_contents($file);
$params = eval('?>' . file_get_contents($file));
if ($params['Job1Version'] != $this->currentVersion) {
echo "not the same version, exit worker \n";
sleep(2);
exit();
} else {
echo "same version, continue processing \n";
}
}
}
}
When I will update the code, the params file will change with a new version number which will force the worker to terminate. I cannot use include as the file will be loaded in memory in the while loop. Knowing that the file params.php isn't critical in terms of security I wanted to know if there was another way of doing so?
Edit: the params.php looks as follow:
<?php
return [
'Job1Version' => 5
];
$params = require($file);
Since your file has a return statement, the returned value will be passed along.
After few tests I finally managed to find a solution which doesn't require versionning anymore.
$reflectionClass = new \ReflectionClass($this);
$lastUpdatedTimeOnStart = filemtime($reflectionClass->getFileName());
while (true) {
clearstatcache();
$reflectionClass = new \ReflectionClass($this);
$lastUpdatedTime = filemtime($reflectionClass->getFileName());
if ($lastUpdatedTime != $lastUpdatedTimeOnStart) {
// An update has been made, exit
} else {
// worker hasn't been modified since running
}
}
Whenever the file will be updated, the worker will automatically exit
Thanks to #Rudie who pointed me into the right direction.

Ambiguity in HMVC Routing

I've a routing mechanism that dispatches requests by relying on the file system structure:
function Route($root) {
$root = realpath($root) . '/';
$segments = array_filter(explode('/',
substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']))
), 'strlen');
if ((count($segments) == 0) || (is_dir($root) === false)) {
return true; // serve index
}
$controller = null;
$segments = array_values($segments);
while ((is_null($segment = array_shift($segments)) !== true)
&& (is_dir($root . $controller . $segment . '/'))) {
$controller .= $segment . '/';
}
if ((is_file($controller = $root . $controller . $segment . '.php')) {
$class = basename($controller . '.php');
$method = array_shift($segments) ?: $_SERVER['REQUEST_METHOD'];
require($controller);
if (method_exists($class = new $class(), $method)) {
return call_user_func_array(array($class, $method), $segments);
}
}
throw new Exception('/' . implode('/', self::Segment()), 404); // serve 404
}
Basically, it tries to map as many URL segments to directories as it can, matching the following segment to the actual controller (.php file with the same name). If more segments are provided, the first defines the action to call (falling back to the HTTP method), and the remaining as the action arguments.
The problem is that (depending on the file system structure) there are some ambiguities. Consider this:
- /controllers
- /admin
- /company
- /edit.php (has get() & post() methods)
- /company.php (has get($id = null) method)
Now the ambiguity - when I access domain.tld/admin/company/edit/ the edit.php controller serves the request (as it should), however accessing domain.tld/admin/company/ via GET or domain.tld/admin/company/get/ directly throws a 404 error because the company segment was mapped to the corresponding directory, even though the remaining segments have no mapping in the file system. How can I solve this issue? Preferably without putting too much effort in the disk.
There are already a lot of similar questions in SO regarding this problem, I looked at some of them but I couldn't find a single answer that provides a reliable and efficient solution.
For critical stuff like this it's really important to write test, with a test Framework like PHPUnit.
Install it like described here (You need pear):
https://github.com/sebastianbergmann/phpunit/
I also use a virtual file system so your test folder doesn't get cluttered: https://github.com/mikey179/vfsStream/wiki/Install
I simply dropped your Route function into a file called Route.php. In the same directory I now created a test.php file with the following content:
<?php
require_once 'Route.php';
class RouteTest extends PHPUnit_Framework_TestCase {
}
To check if it all works open the command line and do the following:
$ cd path/to/directory
$ phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 1.50Mb
There was 1 failure:
1) Warning
No tests found in class "RouteTest".
FAILURES!
Tests: 1, Assertions: 0, Failures: 1.
If this appears PHPUnit is correctly installed and you're ready to write tests.
In order make the Route function better testable and less coupled to server and the file system I modified it slightly:
// new parameter $request instead of relying on server variables
function Route($root, $request_uri, $request_method) {
// vfsStream doesn't support realpath(). This will do.
$root .= '/';
// replaced server variable with $request_uri
$segments = array_filter(explode('/', $request_uri), 'strlen');
if ((count($segments) == 0) || (is_dir($root) === false)) {
return true; // serve index
}
$controller = null;
$all_segments = array_values($segments);
$segments = $all_segments;
while ((is_null($segment = array_shift($segments)) !== true)
&& (is_dir($root . $controller . $segment . '/'))) {
$controller .= $segment . '/';
}
if (is_file($controller = $root . $controller . $segment . '.php')) {
$class = basename($controller . '.php');
// replaced server variable with $request_method
$method = array_shift($segments) ?: $request_method;
require($controller);
if (method_exists($class = new $class(), $method)) {
return call_user_func_array(array($class, $method), $segments);
}
}
// $all_segments variable instead of a call to self::
throw new Exception('/' . implode('/', $all_segments), 404); // serve 404
}
Lets add a test to check wheter the function returns true if the index route is requested:
public function testIndexRoute() {
$this->assertTrue(Route('.', '', 'get'));
$this->assertTrue(Route('.', '/', 'get'));
}
Because your test class extends PHPUnit_Framework_TestCase you can now use methods like $this->assertTrue
to check wheter a certain statement evaluates to true. Lets run it again:
$ phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 1.75Mb
OK (1 test, 2 assertions)
To this test passed! Lets test if array_filter removes empty segments correctly:
public function testEmptySegments() {
$this->assertTrue(Route('.', '//', 'get'));
$this->assertTrue(Route('.', '//////////', 'get'));
}
Lets also test if the index route is requested if the $root directory for the routes doesn't exist.
public function testInexistentRoot() {
$this->assertTrue(Route('./inexistent', '/', 'get'));
$this->assertTrue(Route('./does-not-exist', '/some/random/route', 'get'));
}
To test more stuff than this we now need files containing classes with methods. So let's use our virtual file system to setup a directory structure with files before running each test.
require_once 'Route.php';
require_once 'vfsStream/vfsStream.php';
class RouteTest extends PHPUnit_Framework_TestCase {
public function setUp() {
// intiialize stuff before each test
}
public function tearDown() {
// clean up ...
}
PHPUnit has some special methods for this kind of thing. The setUp method gets executed before every test method in this test class. And the tearDown method after a test method as been executed.
Now I create a directory Structure using vfsStream. (If you are looking for a tutorial to do this: https://github.com/mikey179/vfsStream/wiki is a pretty good resource)
public function setUp() {
$edit_php = <<<EDIT_PHP
<?php
class edit {
public function get() {
return __METHOD__ . "()";
}
public function post() {
return __METHOD__ . "()";
}
}
EDIT_PHP;
$company_php = <<<COMPANY_PHP
<?php
class company {
public function get(\$id = null) {
return __METHOD__ . "(\$id)";
}
}
COMPANY_PHP;
$this->root = vfsStream::setup('controllers', null, Array(
'admin' => Array(
'company' => Array(
'edit.php' => $edit_php
),
'company.php' => $company_php
)
));
}
public function tearDown() {
unset($this->root);
}
vfsStream::setup() now creates a virtual directory with the given file structure and the given file contents.
And as you can see I let my controllers return the name of the method and the parameters as string.
Now we can add a few more tests to our test suite:
public function testSimpleDirectMethodAccess() {
$this->assertEquals("edit::get()", Route(vfsStream::url('controllers'), '/controllers/admin/company/edit/get', 'get'));
}
But this time the test fails:
$ phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
...
Fatal error: Class 'edit.php.php' not found in C:\xampp\htdocs\r\Route.php on line 27
So there is something wrong with the $class variable. If we now inspect the following line in the Route function with a debugger (or some echos).
$class = basename($controller . '.php');
We can see the that the $controller variable holds the correct filename, but why is there a .php appended?
This seams to be a typing mistake. I think it should be:
$class = basename($controller, '.php');
Because this removes the .php extension. And we get the correct classname edit.
Now let's test if an exception gets thrown if we request an random path which doesn't exist in our directory structure.
/**
* #expectedException Exception
* #expectedMessage /random-route-to-the/void
*/
public function testForInexistentRoute() {
Route(vfsStream::url('controllers'), '/random-route-to-the/void', 'get');
}
PHPUnit automaticly reads this comments and checks if an Exception of type Exception is thrown when executing this method and if the message of the Exception was /random-route-to-the/void
This seams to work. Lets check if the $request_method parameter works properly.
public function testMethodAccessByHTTPMethod() {
$this->assertEquals("edit::get()", Route(vfsStream::url('controllers'), '/admin/company/edit', 'get'));
$this->assertEquals("edit::post()", Route(vfsStream::url('controllers'), '/admin/company/edit', 'post'));
}
If we execute this test we run into an other issue:
$ phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
....
Fatal error: Cannot redeclare class edit in vfs://controllers/admin/company/edit.php on line 2
Looks like we use an include/require multiple times for the same file.
require($controller);
Lets change that to
require_once($controller);
Now let's face your issue and write a test to check that the directory company and the file company.php do not interfere with each other.
$this->assertEquals("company::get()", Route(vfsStream::url('controllers'), '/admin/company', 'get'));
$this->assertEquals("company::get()", Route(vfsStream::url('controllers'), '/admin/company/get', 'get'));
And here we get the 404 Exception, as you stated in your question:
$ phpunit test.php
PHPUnit 3.7.13 by Sebastian Bergmann.
.....E.
Time: 0 seconds, Memory: 2.00Mb
There was 1 error:
1) RouteTest::testControllerWithSubControllers
Exception: /admin/company
C:\xampp\htdocs\r\Route.php:32
C:\xampp\htdocs\r\test.php:69
FAILURES!
Tests: 7, Assertions: 10, Errors: 1.
The problem right here is, we don't know excatly when to enter the subdirectory and when to use the controller in the .php file.
So we need to specify what exactly you want to happen. And I assume the following, because it makes sense.
Only enter a subdirectory if the controller doesn't contain the method requested.
If neither the controller nor the subdirectory contains the method requested throw a 404
So instead of searching directories like here:
while ((is_null($segment = array_shift($segments)) !== true)
&& (is_dir($root . $controller . $segment . '/'))) {
$controller .= $segment . '/';
}
We need to search for files. And if we find a file which doesn't contain the method requested, then we search for a directory.
function Route($root, $request_uri, $request_method) {
$segments = array_filter(explode('/', $request_uri), 'strlen');
if ((count($segments) == 0) || (is_dir($root) === false)) {
return true; // serve index
}
$all_segments = array_values($segments);
$segments = $all_segments;
$directory = $root . '/';
do {
$segment = array_shift($segments);
if(is_file($controller = $directory . $segment . ".php")) {
$class = basename($controller, '.php');
$method = isset($segments[0]) ? $segments[0] : $request_method;
require_once($controller);
if (method_exists($class = new $class(), $method)) {
return call_user_func_array(array($class, $method), array_slice($segments, 1));
}
}
$directory .= $segment . '/';
} while(is_dir($directory));
throw new Exception('/' . implode('/', $all_segments), 404); // serve 404
}
This method works now as expected.
We could now add a lot more test cases, but I don't want to stretch this more.
As you can see it's very useful to run a set of automated tests to ensure
that some things in your function work. It's also very helpful for debugging, because
you get to know where exactly the error occured. I just wanted to give you a start on
how to do TDD and how to use PHPUnit, so you can debug your code yourself.
"Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime."
Of course you should write the tests before you write the code.
Here a few more links which could be interesting:
PHPUnit Manual: http://www.phpunit.de/manual/current/en/
Official PEAR Website: http://pear.php.net/
Test Driven Development (TDD): http://en.wikipedia.org/wiki/Test-driven_development
Although your method of magic HVMC is convenient for developers.. it could become a bit of a performance killer (all the stats/lstats). I once used a similar method of mapping FS to routes but later gave up on the magic and replaced it with some good old fashion hard coded config:
$controller_map = array(
'/some/route/' => '/some/route.php',
'/anouther/route/' => 'another/route.php',
# etc, etc, ...
);
Perhaps it's not as elegant as what you have in place and will require some config changes everytime you add/remove a controller (srsly, this shouldn't be a common task..) but it is faster, removes all ambiguities, and gets rid of all of the useless disk/page-cache lookups.
Sorry, I haven't had the time to test my solution, but here is my suggestion:
while ((is_null($segment = array_shift($segments)) !== true)
&& (is_dir($root . $controller . $segment . '/'))
&& ( (is_file($controller = $root . $controller . $segment . '.php')
&& (!in_array(array_shift(array_values($segments)), ['get','post']) || count($segments)!=0 ) ) ) {
$controller .= $segment . '/';
}
A simple explanation to the above code would be that if a route that is both a file and a directory is encountered, check if it is succeeded by get / post or if it is the last segment in the $segments array. If it is, treat it as a file, otherwise, keep adding segments to the $controller variable.
Although the code sample I gave is simply what I had in mind, it has not been tested. However, if you use this workflow in the comparison, you should be able to pull it off. I suggest you follow smassey's answer and keep to declaring routes for each controller.
Note: I am using *array_shift* on *array_values* so I only pull the next segment's value without tampering with the $segments array. [edit]

Why some classes are not loaded from my libraries on CodeIgniter

I'm trying to integrate Evernote SDK to my CodeIgniter web application and some classes from the library are loaded and others not, :-S I can't see why.
I have that simple piece of code:
$access_token = 'my validated access token ';
// Get User Store
$userStoreTrans;
try{
$userStoreTrans = new THttpClient(USER_STORE_HOST, USER_STORE_PORT, USER_STORE_URL, USER_STORE_PROTO);
}
catch(TTransportException $e)
{
print $e->errorCode.' Message:'.$e->parameter;
}
$userStoreProt = new TBinaryProtocol($userStoreTrans);
$userStoreClient = new UserStoreClient($userStoreProt, $userStoreProt);
While $userStoreTrans and $userStoreProt are created correctly, a THttpClient and TBinaryProtocol classes from Evernote SDK, $userStoreClient throws a Class 'UserStoreClient' not found in .../home.php
I don't understand why some classes are recognized and some others not, the main difference that I can see is that "TClasses" are under evernote-sdk-php/lib/transport/*.php and evernote-sdk-php/lib/protocol/*.php and UserStoreClient has an extra folder evernote-sdk-php/lib/packages/UserStore/*.php
I'll explain how I'm including evernote-sdk-php to my CodeIgniter installation:
This is my CodeIgniter config/autoload.php
$autoload['libraries'] = array('database','session','form_validation','security','tank_auth','Evernote_bootstrap');
This is my Evernote_bootstrap.php file
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
define("EVERNOTE_LIBS", dirname(__FILE__) . DIRECTORY_SEPARATOR . "evernote-sdk-php/lib");
// add ourselves to include path
ini_set("include_path", ini_get("include_path") . ":" . EVERNOTE_LIBS);
require_once("evernote-sdk-php/lib/autoload.php");
require_once("evernote-oauth/functions.php");
class Evernote_Bootstrap
{
function __construct()
{
// log_message('debug','Evernote_Bootstrap');
}
}
?>
The main purpose of Evernote_Bootstrap class is the require_once of evernote-sdk-php/lib/autoload.php, this class is auto-generated using the -phpa Thrift generator flag, I only add some logging to see the problem.
autoload.php:
<?php
/**
* Copyright (c) 2006- Facebook
* Distributed under the Thrift Software License
*
* See accompanying file LICENSE or visit the Thrift site at:
* http://developers.facebook.com/thrift/
*
* #package thrift
* #author Mark Slee <mcslee#facebook.com>
*/
/**
* Include this file if you wish to use autoload with your PHP generated Thrift
* code. The generated code will *not* include any defined Thrift classes by
* default, except for the service interfaces. The generated code will populate
* values into $GLOBALS['THRIFT_AUTOLOAD'] which can be used by the autoload
* method below. If you have your own autoload system already in place, rename your
* __autoload function to something else and then do:
* $GLOBALS['AUTOLOAD_HOOKS'][] = 'my_autoload_func';
*
* Generate this code using the -phpa Thrift generator flag.
*/
/**
* This parses a given filename for classnames and populates
* $GLOBALS['THRIFT_AUTOLOAD'] with key => value pairs
* where key is lower-case'd classname and value is full path to containing file.
*
* #param String $filename Full path to the filename to parse
*/
function scrapeClasses($filename) {
$fh = fopen($filename, "r");
while ($line = fgets($fh)) {
$matches = array();
if ( preg_match("/^\s*class\s+([^\s]+)/", $line, $matches)) {
if (count($matches) > 1)
$GLOBALS['THRIFT_AUTOLOAD'][strtolower($matches[1])] = $filename;
}
}
}
function findFiles($dir, $pattern, &$finds) {
if (! is_dir($dir))
return;
if (empty($pattern))
$pattern = "/^[^\.][^\.]?$/";
$files = scandir($dir);
if (!empty($files)) {
foreach ($files as $f) {
if ($f == "." || $f == "..")
continue;
if ( is_file($dir . DIRECTORY_SEPARATOR . $f) && preg_match($pattern, $f)) {
$finds[] = $dir . DIRECTORY_SEPARATOR . $f;
} else if ( is_dir($dir . DIRECTORY_SEPARATOR . $f) && substr($f, 0, 1) != ".") {
findFiles($dir . DIRECTORY_SEPARATOR . $f, $pattern, $finds);
}
}
}
}
function str_var_dump($object)
{
ob_start();
var_dump($object);
$dump = ob_get_clean();
return $dump;
}
// require Thrift core
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . "Thrift.php");
if (! isset($GLOBALS['THRIFT_ROOT']))
$GLOBALS['THRIFT_ROOT'] = dirname(__FILE__);
log_message('debug','bootstrap autoload.php is executing');
// stuff for managing autoloading of classes
$GLOBALS['THRIFT_AUTOLOAD'] = array();
$GLOBALS['AUTOLOAD_HOOKS'] = array();
$THRIFT_AUTOLOAD =& $GLOBALS['THRIFT_AUTOLOAD'];
// only populate if not done so already
if (empty($GLOBALS['THRIFT_AUTOLOAD'])) {
//$allLibs = glob( dirname(__FILE__) . "/**/*.php"); // oh poor winblows users can't use glob recursively
$allLibs = array();
findFiles( dirname(__FILE__), "/\.php$/i", $allLibs);
log_message('debug',str_var_dump($allLibs));
if (!empty($allLibs)) {
foreach ($allLibs as $libFile) {
scrapeClasses($libFile);
}
log_message('debug','all scrapped classes: ' . str_var_dump($GLOBALS['THRIFT_AUTOLOAD']));
}
}else{
log_message('debug','$GLOBALS[THRIFT_AUTOLOAD] already defined');
}
// main autoloading
if (!function_exists('__autoload')) {
function __autoload($class) {
log_message('debug','__autoload');
global $THRIFT_AUTOLOAD;
$classl = strtolower($class);
if (isset($THRIFT_AUTOLOAD[$classl])) {
// log_message('debug','$THRIFT_AUTOLOAD[$classl] is set, do require_once');
//include_once $GLOBALS['THRIFT_ROOT'].'/packages/'.$THRIFT_AUTOLOAD[$classl];
require_once($THRIFT_AUTOLOAD[$classl]);
} else if (!empty($GLOBALS['AUTOLOAD_HOOKS'])) {
log_message('debug','$GLOBALS[AUTOLOAD_HOOKS]is no empty, lets foreach');
foreach ($GLOBALS['AUTOLOAD_HOOKS'] as $hook) {
// log_message('debug','iterate');
$hook($class);
}
} else {
log_message('debug','nothing to do');
}
}
}
Then I logged that library and seems to work fine. You can see the main important logs: log_message('debug',str_var_dump($allLibs)); and log_message('debug','all scrapped classes: ' . str_var_dump($GLOBALS['THRIFT_AUTOLOAD'])); and the output: http://pastebin.com/8w1MCKx9:
As you can see, UserStore class seems to be equally fine loaded than THttpClient or TBinaryProtocol... any idea about the problem?
I don't know if is important but I notice that $GLOBALS['THRIFT_ROOT'], defined on autoload.php, is not accesible from a CI Controller. Probably I'm missing something about CI architecture.
As of our lastest SDK updates, UserStoreClient (and the other SDK classes) are in appropriate namespaces. Assuming that you're using our generated code, have you imported the classes you're using? E.g.
use EDAM\UserStore\UserStoreClient;

integrating google minify with zend framework

so I tried to integrate minify and zend framework
http://code.google.com/p/minify/
what I basically did was copy the contents of minify's index.php file to a zend action:
and changed the third line from
define('MINIFY_MIN_DIR', dirname(__FILE__));
to
define('MINIFY_MIN_DIR', 'Z:\wamp2\www\min');
which is where the minify folder is located
here's the full action:
public function test2Action()
{
define('MINIFY_MIN_DIR', 'Z:\wamp2\www\min');
// load config
require MINIFY_MIN_DIR . '/config.php';
// setup include path
set_include_path($min_libPath . PATH_SEPARATOR . get_include_path());
require 'Minify.php';
Minify::$uploaderHoursBehind = $min_uploaderHoursBehind;
Minify::setCache(
isset($min_cachePath) ? $min_cachePath : ''
,$min_cacheFileLocking
);
if ($min_documentRoot) {
$_SERVER['DOCUMENT_ROOT'] = $min_documentRoot;
} elseif (0 === stripos(PHP_OS, 'win')) {
Minify::setDocRoot(); // IIS may need help
}
$min_serveOptions['minifierOptions']['text/css']['symlinks'] = $min_symlinks;
if ($min_allowDebugFlag && isset($_GET['debug'])) {
$min_serveOptions['debug'] = true;
}
if ($min_errorLogger) {
require_once 'Minify/Logger.php';
if (true === $min_errorLogger) {
require_once 'FirePHP.php';
Minify_Logger::setLogger(FirePHP::getInstance(true));
} else {
Minify_Logger::setLogger($min_errorLogger);
}
}
// check for URI versioning
if (preg_match('/&\\d/', $_SERVER['QUERY_STRING'])) {
$min_serveOptions['maxAge'] = 31536000;
}
if (isset($_GET['g'])) {
// well need groups config
$min_serveOptions['minApp']['groups'] = (require MINIFY_MIN_DIR . '/groupsConfig.php');
}
if (isset($_GET['f']) || isset($_GET['g'])) {
// serve!
Minify::serve('MinApp', $min_serveOptions);
//echo Minify::combine(array('//css/DisplayHelpers/DisplayObject.css'),$min_serveOptions);
} else{
echo 'fail';
}
// action body
}
notice these lines...I added the combine line which is commented out
Minify::serve('MinApp', $min_serveOptions);
//echo Minify::combine(array('//css/DisplayHelpers/DisplayObject.css'),$min_serveOptions);
the
Minify::serve('MinApp', $min_serveOptions); line is in the original index.php....if I keep it there, it will not return the correct minifed files properly and instead return some crazy gibberish when I go to http://localhost/tester/test2?f=/css/DisplayHelpers/DisplayObject.css...
on the other hand, when I go to http://localhost/min?f=/css/DisplayHelpers/DisplayObject.css which uses minify's index.php it would work properly
on the other hand if I uncomment the combine line and comment the serve line it would also work properly but it won't do caching, etc
any ideas on how to resolve in using the normal serve method within the zend action so that I can use the cache?
Why an action plus there's already a Zend helper in that project.

Running a Zend Framework action from command line

I would like to run a Zend Framework action to generate some files, from command line. Is this possible and how much change would I need to make to my existing Web project that is using ZF?
Thanks!
UPDATE
You can have all this code adapted for ZF 1.12 from https://github.com/akond/zf-cli if you like.
While the solution #1 is ok, sometimes you want something more elaborate.
Especially if you are expecting to have more than just one CLI script.
If you allow me, I would propose another solution.
First of all, have in your Bootstrap.php
protected function _initRouter ()
{
if (PHP_SAPI == 'cli')
{
$this->bootstrap ('frontcontroller');
$front = $this->getResource('frontcontroller');
$front->setRouter (new Application_Router_Cli ());
$front->setRequest (new Zend_Controller_Request_Simple ());
}
}
This method will deprive dispatching control from default router in favour of our own router Application_Router_Cli.
Incidentally, if you have defined your own routes in _initRoutes for your web interface, you would probably want to neutralize them when in command-line mode.
protected function _initRoutes ()
{
$router = Zend_Controller_Front::getInstance ()->getRouter ();
if ($router instanceof Zend_Controller_Router_Rewrite)
{
// put your web-interface routes here, so they do not interfere
}
}
Class Application_Router_Cli (I assume you have autoload switched on for Application prefix) may look like:
class Application_Router_Cli extends Zend_Controller_Router_Abstract
{
public function route (Zend_Controller_Request_Abstract $dispatcher)
{
$getopt = new Zend_Console_Getopt (array ());
$arguments = $getopt->getRemainingArgs ();
if ($arguments)
{
$command = array_shift ($arguments);
if (! preg_match ('~\W~', $command))
{
$dispatcher->setControllerName ($command);
$dispatcher->setActionName ('cli');
unset ($_SERVER ['argv'] [1]);
return $dispatcher;
}
echo "Invalid command.\n", exit;
}
echo "No command given.\n", exit;
}
public function assemble ($userParams, $name = null, $reset = false, $encode = true)
{
echo "Not implemented\n", exit;
}
}
Now you can simply run your application by executing
php index.php backup
In this case cliAction method in BackupController controller will be called.
class BackupController extends Zend_Controller_Action
{
function cliAction ()
{
print "I'm here.\n";
}
}
You can even go ahead and modify Application_Router_Cli class so that not "cli" action is taken every time, but something that user have chosen through an additional parameter.
And one last thing. Define custom error handler for command-line interface so you won't be seeing any html code on your screen
In Bootstrap.php
protected function _initError ()
{
$error = $frontcontroller->getPlugin ('Zend_Controller_Plugin_ErrorHandler');
$error->setErrorHandlerController ('index');
if (PHP_SAPI == 'cli')
{
$error->setErrorHandlerController ('error');
$error->setErrorHandlerAction ('cli');
}
}
In ErrorController.php
function cliAction ()
{
$this->_helper->viewRenderer->setNoRender (true);
foreach ($this->_getParam ('error_handler') as $error)
{
if ($error instanceof Exception)
{
print $error->getMessage () . "\n";
}
}
}
It's actually much easier than you might think. The bootstrap/application components and your existing configs can be reused with CLI scripts, while avoiding the MVC stack and unnecessary weight that is invoked in a HTTP request. This is one advantage to not using wget.
Start your script as your would your public index.php:
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV',
(getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
: 'production'));
require_once 'Zend/Application.php';
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/config.php'
);
//only load resources we need for script, in this case db and mail
$application->getBootstrap()->bootstrap(array('db', 'mail'));
You can then proceed to use ZF resources just as you would in an MVC application:
$db = $application->getBootstrap()->getResource('db');
$row = $db->fetchRow('SELECT * FROM something');
If you wish to add configurable arguments to your CLI script, take a look at Zend_Console_Getopt
If you find that you have common code that you also call in MVC applications, look at wrapping it up in an object and calling that object's methods from both the MVC and the command line applications. This is general good practice.
Just saw this one get tagged in my CP. If you stumbled onto this post and are using ZF2, it's gotten MUCH easier. Just edit your module.config.php's routes like so:
/**
* Router
*/
'router' => array(
'routes' => array(
// .. these are your normal web routes, look further down
),
),
/**
* Console Routes
*/
'console' => array(
'router' => array(
'routes' => array(
/* Sample Route */
'do-cli' => array(
'options' => array(
'route' => 'do cli',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'do-cli',
),
),
),
),
),
),
Using the config above, you would define doCliAction in your IndexController.php under your Application module. Running it is cake, from the command line:
php index.php do cli
Done!
Way smoother.
akond's solution above is on the best track, but there are some subtleties that may may his script not work in your environment. Consider these tweaks to his answer:
Bootstrap.php
protected function _initRouter()
{
if( PHP_SAPI == 'cli' )
{
$this->bootstrap( 'FrontController' );
$front = $this->getResource( 'FrontController' );
$front->setParam('disableOutputBuffering', true);
$front->setRouter( new Application_Router_Cli() );
$front->setRequest( new Zend_Controller_Request_Simple() );
}
}
Init error would probably barf as written above, the error handler is probably not yet instantiated unless you've changed the default config.
protected function _initError ()
{
$this->bootstrap( 'FrontController' );
$front = $this->getResource( 'FrontController' );
$front->registerPlugin( new Zend_Controller_Plugin_ErrorHandler() );
$error = $front->getPlugin ('Zend_Controller_Plugin_ErrorHandler');
$error->setErrorHandlerController('index');
if (PHP_SAPI == 'cli')
{
$error->setErrorHandlerController ('error');
$error->setErrorHandlerAction ('cli');
}
}
You probably, also, want to munge more than one parameter from the command line, here's a basic example:
class Application_Router_Cli extends Zend_Controller_Router_Abstract
{
public function route (Zend_Controller_Request_Abstract $dispatcher)
{
$getopt = new Zend_Console_Getopt (array ());
$arguments = $getopt->getRemainingArgs();
if ($arguments)
{
$command = array_shift( $arguments );
$action = array_shift( $arguments );
if(!preg_match ('~\W~', $command) )
{
$dispatcher->setControllerName( $command );
$dispatcher->setActionName( $action );
$dispatcher->setParams( $arguments );
return $dispatcher;
}
echo "Invalid command.\n", exit;
}
echo "No command given.\n", exit;
}
public function assemble ($userParams, $name = null, $reset = false, $encode = true)
{
echo "Not implemented\n", exit;
}
}
Lastly, in your controller, the action that you invoke make use of the params that were orphaned by the removal of the controller and action by the CLI router:
public function echoAction()
{
// disable rendering as required
$database_name = $this->getRequest()->getParam(0);
$udata = array();
if( ($udata = $this->getRequest()->getParam( 1 )) )
$udata = explode( ",", $udata );
echo $database_name;
var_dump( $udata );
}
You could then invoke your CLI command with:
php index.php Controller Action ....
For example, as above:
php index.php Controller echo database123 this,becomes,an,array
You'll want to implement a more robust filtering/escaping, but, it's a quick building block. Hope this helps!
One option is that you could fudge it by doing a wget on the URL that you use to invoke the desirable action
You cant use -O option of wget to save the output. But wget is clearly NOT the solution. Prefer using CLI instead.
akond idea works great, except the error exception isnt rendered by the error controller.
public function cliAction() {
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
foreach ($this->_getParam('error_handler') as $error) {
if ($error instanceof Exception) {
print "cli-error: " . $error->getMessage() . "\n";
}
}
}
and In Application_Router_Cli, comment off the echo and die statement
public function assemble($userParams, $name = null, $reset = false, $encode = true) {
//echo "Not implemented\n";
}
You can just use PHP as you would normally from the command line. If you call a script from PHP and either set the action in your script you can then run whatever you want.
It would be quite simple really.
Its not really the intended usage, however this is how it could work if you wanted to.
For example
php script.php
Read here: http://php.net/manual/en/features.commandline.php
You can use wget command if your OS is Linux. For example:
wget http://example.com/controller/action
See http://linux.about.com/od/commands/l/blcmdl1_wget.htm
UPDATE:
You could write a simple bash script like this:
if wget http://example.com/controller/action
echo "Hello World!" > /home/wasdownloaded.txt
else
"crap, wget timed out, let's remove the file."
rm /home/wasdownloaded.txt
fi
Then you can do in PHP:
if (true === file_exists('/home/wasdownloaded.txt') {
// to check that the
}
Hope this helps.
I have used wget command
wget http://example.com/module/controller/action -O /dev/null
-O /dev/null if you dont want to save the output

Categories