Class not found in Bootstrap.php in CLI mode (Zend) - php

I am getting an error when trying to run app in CLI mode. First of all I have daemon controller which allows to run daemon instances (objects with only one required method 'run' derived from my daemon interface) using url /daemon-cli/:Daemon_Class_To_Run. So there is nothing interesting from this side.
Also I have little sh script to run this controller in a simplified way like this:
# ./daemon.sh Ami_Daemon_EventParser
This returns me the next error:
<?
class System_Controller_Plugin_ModuleLayoutLoader extends Zend_Controller_Plugin_Abstract {
/**
* Array of layout paths associating modules with layouts
*/
protected $_moduleLayouts;
// here was the full code of this class
}
PHP Fatal error: Class 'System_Controller_Plugin_ModuleLayoutLoader' not found in /var/www/htdocs/application/Bootstrap.php on line 99
System namespace is located in application/library/System and this is just a set of toolkits and libraries. That is completely weird because it works well in apache2handler mode, but crashes in cli. I don't understand how the class cannot be found if the code of "not founded" class are returned in the error. I suppose I bootstrapped application in a wrong way.
Some of bootstrap code:
protected function _initAppModules()
{
$modules = array();
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('System_');
$includePath = '';
// Add model directories to include path
if ($dhApp = opendir(APPLICATION_PATH . '/modules')) {
while (($dirApp = readdir($dhApp)) !== false) {
if (substr($dirApp, 0, 1) != '.' && is_dir(APPLICATION_PATH . '/modules/' . $dirApp)) {
$modules [] = $dirApp;
if ($dhModule = opendir(APPLICATION_PATH . '/modules/' . $dirApp)) {
while (($dirModule = readdir($dhModule)) !== false) {
if ($dirModule == 'library' && is_dir(APPLICATION_PATH . '/modules/' . $dirApp . '/library')) {
$includePath .= PATH_SEPARATOR . APPLICATION_PATH . '/modules/' . $dirApp . '/library';
$autoloader->registerNamespace(ucfirst($dirApp));
}
}
closedir($dhModule);
}
}
}
closedir($dhApp);
}
ini_set('include_path', ini_get('include_path') . $includePath);
return $modules;
}
protected function _initResources()
{
Zend_Controller_Action_HelperBroker::addPrefix('System_Controller_Action_Helper');
$resourceLoader = new Zend_Loader_Autoloader_Resource(array('basePath' => APPLICATION_PATH . '/library/System', 'namespace' => ''));
$resourceLoader->addResourceTypes(array('form' => array('path' => 'Form/Resource/', 'namespace' => 'Form'), 'model' => array('path' => 'Model/Resource/', 'namespace' => 'Model')));
System_API_Abstract::load();
return $resourceLoader;
}
protected function _initView(array $options = array())
{
$this->bootstrap('AppModules');
$this->bootstrap('FrontController');
$view = new Zend_View();
$view->addScriptPath(APPLICATION_PATH . '/views/scripts');
$view->addHelperPath(APPLICATION_PATH . '/views/helpers', 'View_Helper');
$view->addHelperPath(APPLICATION_PATH . '/modules/gui/views/helpers', 'View_Helper');
$view->addHelperPath(APPLICATION_PATH . '/library/System/View/Helper', 'System_View_Helper');
// next line triggers the error
$layoutModulePlugin = new System_Controller_Plugin_ModuleLayoutLoader();
foreach ($this->getResource('AppModules') as $module) {
if (is_dir(APPLICATION_PATH . '/modules/' . $module . '/views/layouts')) {
$layoutModulePlugin->registerModuleLayout($module, APPLICATION_PATH . '/modules/' . $module . '/views/layouts');
}
if (is_dir(APPLICATION_PATH . '/modules/' . $module . '/views/helpers')) {
$view->addHelperPath(APPLICATION_PATH . '/modules/' . $module . '/views/helpers', ucfirst($module) . '_View_Helper');
}
}
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->setView($view);
Zend_Layout::startMvc(array('layoutPath' => '../application/views/layouts', 'layout' => 'layout'));
$this->getResource('FrontController')->registerPlugin($layoutModulePlugin);
return $view;
}
And in the index.php I bootstrap all modules.
if (php_sapi_name() == 'cli') {
$_SERVER['REQUEST_URI'] = $argv[1];
$_SERVER ['HTTP_HOST'] = 'mydomain.info';
ini_set('session.use_cookies', false);
$application->bootstrap()->run();// in case if I find the way to bootstrap partially
} else {
$application->bootstrap()->run();
}
I am fighting with this for a couple of weeks, so I hope somebody has an answer or a good advice. Any help is greatly appreciated. Thanks!

Finally I did that!
As you can see I had a php short tag in the error output. Php often uses separate php.ini for cli mode as my rackspace distributive does. Php distros for windows usually have the common ini file.

Related

Getting error using $this when not in object context to all methods in a class except __construct() method

I have two classes in separate files. The names of classes and files are: Substitute.php and Models.php. I have some properties in class Substitute.php which I can access using $this->property_name in the __construct() method without any error. However when I try to access the same property in the another method(method test() in my case(see the code below)) in the same class (i.e., class Substitute) I get the error Fatal error: Uncaught Error: Using $this when not in object context in G:\ROHAN\eTh0\VectorVolunteers\vector\backend\models\Substitute.php:22. Line 22 being echo $this->c_day; in the method sss() (I have cut some of the code in the class, adding only the which I thought to be relevant, I'll add rest of the code in case someone requires). I access methods sss() and test() from the url by autoloader method and class Router which I have included below too:
Below this line lies the code inside Substitute.php:
EDIT1: I have added another method from Substitute class where also I get the same error.
class Substitute extends Models{
public $user_id, $c_day, $c_date;
public function __construct(){
$_SESSION['username']='user2';
echo 'constructor in class Substitute ran';
$this->user_id = $this->get_user_id();
$this->c_day = $this->upcoming_class();
}
public function sss(){
echo $this->c_day;
}
public function test(){
if(isset($_POST['select_class_subs'])){
switch ($_POST['select_class_subs']) {
case 'Next Class':
$this->c_date = date('d-M-yy', strtotime(($this->c_day)));
break;
case 'Next-to-next Class':
$this->c_date = date('d-M-yy', strtotime(($this->c_day . '+1 week')));
break;
default:
echo 'Custom class selected <br>';
break;
}
if($this->c_date==date('d-M-yy')){
echo "Sorry, but you cannot post a substitute request for a class that is scheduled to happen on the same day. <br>";
}
else{
echo "Done! <br>";
}
}
}
EDIT2: The code in the file config.php which is required in the autoloader() method(See below):
if(isset($_GET['url'])){$url = explode('/', $_GET['url']);}
require_once (ROOT . DS . 'backend' . DS . 'bootstrap.php');
EDIT2: The code of file bootstrap.php which has autloader() method:
require_once 'config.php';
// Autoloader for classes
spl_autoload_register('autoloader');
function autoloader($class_Name){
if (file_exists (ROOT . DS . 'backend' . DS .'core' . DS . $class_Name . '.php')){
require_once (ROOT . DS . 'backend' . DS . 'core' . DS . $class_Name . '.php');
}
elseif (file_exists (ROOT . DS . 'backend' . DS .'models' . DS . $class_Name . '.php')){
require_once (ROOT . DS . 'backend' . DS . 'models' . DS . $class_Name . '.php');
}
elseif (file_exists(ROOT . DS . 'backend' . DS .'views' . DS . $class_Name . '.php')){
require_once (ROOT . DS . 'backend' . DS . 'views' . DS . $class_Name . '.php');
}
elseif (file_exists(ROOT . DS . 'backend' . DS .'controllers' . DS . $class_Name . '.php')){
require_once (ROOT . DS . 'backend' . DS . 'controllers' . DS . $class_Name . '.php');
}
elseif (file_exists(ROOT . DS . 'backend' . DS .'database' . DS . $class_Name . '.php')) {
require_once (ROOT . DS . 'backend' . DS .'database' . DS . $class_Name . '.php');
}
elseif (file_exists(ROOT . DS . 'backend' . DS .'reminders' . DS . $class_Name . '.php')) {
require_once (ROOT . DS . 'backend' . DS .'reminders' . DS . $class_Name . '.php');
}
else {echo 'Class does not exist or Class file not found' . '<br>';}
}
// Route the request to router.php
if (isset($_GET['url'])) {
Router::route($url);
}
EDIT2: Code in the file that contains class Router{}:
class Router{
private static $controller_name, $method_name;
public static function route($url){
if (isset($url[0]) && $url[0] != "") {
$controller = ucwords($url[0]);
self::$controller_name = $controller;
array_shift($url);
}
if (isset($url[0]) && $url[0] != "") {
$method = ucwords($url[0]);
self::$method_name = $method;
array_shift($url);
}
$params = $url;
$init = new self::$controller_name;
if (method_exists(self::$controller_name, self::$method_name)){
call_user_func_array([self::$controller_name, self::$method_name], $params);
}
else{
if(self::$method_name!=NULL){
echo 'The requested method "' . self::$method_name . '" does not exist in ' . '"' . self::$controller_name . '" controller.';
}
}
}
Below this lies the code inside Models.php (I have included it in case someone requires. But know that I have extended this class to many other classes and all of the methods in this class work fine without any errors or warning).
class Models extends Database {
private $db, $user_id, $table, $rv_id_table_name, $av_id_table_name, $class_day;
protected function get_user_id(){
if (isset($_SESSION['username'])){
$username=$_SESSION['username'];
$sql = 'SELECT * FROM volunteers where username=:username';
$params = [
'username' => $username
];
$array = $this->query($sql, $params);
$this->user_id = $array['id'];
return $this->user_id;
}
else{
echo 'You are not signed in.' . '<br>' . 'Please sign in first.';
}
}
protected function upcoming_class(){
if (isset($_SESSION['username'])) {
$id = $this->get_user_id();
$params = [
'id' => $id
];
$sql = 'SELECT class_day FROM volunteers where id=:id';
$this->class_day = $this->query($sql, $params);
return $this->class_day['class_day'];
}
}
I want to know why am I getting the above mentioned error (in Italics) and how to remove it. I asked it because I have been trying since yesterday, with no success.
How do you use that method (sss)? Are you by chance calling it statically:
Substitute::sss();
If so, that is why you are having the issue. The method is not static. It must be called like this (as one example):
$object = new Substitute();
$object->sss();
I tried converting the properties $c_day and $c_date from class Substitute{} from public to public static and instead of using $this-> to above properties I used self:: in the methods public function sss() and public function test() and to my surprise it works. No more errors.

Restrict my spl_autoloader to only load classes in my namespace?

I've just begun using autoloader lazy loading in my app, and I'm running afoul of namespacing. The autoloader is trying to load things like new DateTime() and failing. Is there a trick to making my autoloader spcific to only my own namespaced classes?
Here is the code I have currently. I suspect it is a mess, but I'm not seeing just how to correct it:
<?php namespace RSCRM;
class Autoloader {
static public function loader($className) {
$filename = dirname(__FILE__) .'/'. str_replace("\\", '/', $className) . ".php";
if (file_exists($filename)) {
include_once($filename);
if (class_exists($className)) {
return TRUE;
}
}
return FALSE;
}
}
spl_autoload_register('\RSCRM\Autoloader::loader');
Happy to RTM if someone can point to a solid example.
What I use is actually adapted from the autoloader used to Unit Test a few of the AuraPHP libraries:
<?php
spl_autoload_register(function ($class) {
// a partial filename
$part = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
// directories where we can find classes
$dirs = array(
__DIR__ . DIRECTORY_SEPARATOR . 'src',
__DIR__ . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'src',
__DIR__ . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR . 'src',
);
// go through the directories to find classes
foreach ($dirs as $dir) {
$file = $dir . DIRECTORY_SEPARATOR . $part;
if (is_readable($file)) {
require $file;
return;
}
}
});
Just make sure the array of '$dirs' values point to the root of your namespaced code.
You can also take a look at the PSR-0 example implementation (http://www.php-fig.org/psr/psr-0/).
You might also want to look an into existing autoloader, like Aura.Autoload or the Symfony ClassLoader Component, although those might be overkill depending on what your requirements are.
I hope this helps.

How to run a script which uses ZendFramework library from CLI

I intend to run a script using ZendFramework library from CLI.
My script is as following:
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Rest_Client');
It can run in a browser, but failed in a command prompt. How can I let the script run from CLI?
Thanks!
this post has some the info you are looking for: Running a Zend Framework action from command line
Below you will find complete functional code that I wrote and use for cron jobs within my apps...
Need to do the following:
pull required files in you cli file
initialize the application and bootstrap resources. Here you can capture cli params and setup request object so it is dispatched properly.
set controller directory
run the application
Here is documentation on Zend_Console_Getopt that will help you understand how to work with cli params.
code for cli.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', 'development');
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/libraries'),
get_include_path(),
)));
// initialize application
require_once 'My/Application/Cron.php';
$application = new My_Application_Cron(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
$application->bootstrap();
//
Zend_Controller_Front::getInstance()->setControllerDirectory(APPLICATION_PATH . '/../scripts/controllers');
//
$application->run();
My_Application_Cron class code:
<?php
// because we are extending core application file we have to explicitly require it
// the autoloading occurs in the bootstrap which is part of this class
require_once 'Zend/Application.php';
class My_Application_Cron extends Zend_Application
{
protected $_cliRequest = null;
protected function _bootstrapCliRequest()
{
$this->_isCliBootstapped = true;
try {
$opts = array(
'help|h' => 'Displays usage information.',
'action|a=s' => 'Action to perform in format of module.controller.action',
'params|p=p' => 'URL-encoded parameter list',
//'verbose|v' => 'Verbose messages will be dumped to the default output.',
);
$opts = new Zend_Console_Getopt($opts);
$opts->setOption('ignoreCase', true)
->parse();
} catch (Zend_Console_Getopt_Exception $e) {
exit($e->getMessage() . "\n\n" . $e->getUsageMessage());
}
// See if help needed
if (isset($opts->h)) {
$prog = $_SERVER['argv'][0];
$msg = PHP_EOL
. $opts->getUsageMessage()
. PHP_EOL . PHP_EOL
. 'Examples:' . PHP_EOL
. "php $prog -a index.index'" . PHP_EOL
. "php $prog -a index.index -p 'fname=John&lname=Smith'" . PHP_EOL
. "php $prog -a index.index -p 'name=John+Smith'" . PHP_EOL
. "php $prog -a index.index -p 'name=John%20Smith'" . PHP_EOL
. PHP_EOL;
echo $msg;
exit;
}
// See if controller/action are set
if (isset($opts->a)) {
// Prepare necessary variables
$params = array();
$reqRoute = array_reverse(explode('.', $opts->a));
#list($action, $controller, $module) = $reqRoute;
// check if request parameters were sent
if ($opts->p) {
parse_str($opts->p, $params);
}
//
$this->_cliRequest = new Zend_Controller_Request_Simple($action, $controller, $module);
$this->_cliRequest->setParams($params);
}
}
public function bootstrap($resource = null)
{
$this->_bootstrapCliRequest();
return parent::bootstrap($resource);
}
public function run()
{
// make sure bootstrapCliRequest was executed prior to running the application
if (!($this->_cliRequest instanceof Zend_Controller_Request_Simple)) {
throw new Exception('application required "bootstrapCliRequest"');
}
// set front controller to support CLI
$this->getBootstrap()->getResource('frontcontroller')
->setRequest($this->_cliRequest)
->setResponse(new Zend_Controller_Response_Cli())
->setRouter(new Custom_Controller_Router_Cli())
->throwExceptions(true);
// run the application
parent::run();
}
}
To lean how to run the cli scipt simply type command below in terminal:
#> php /path/to/cli.php -h
Hopefully this will help you and others!

PHP: autoloading multiple classes with namespaces

I'm trying to build my own framework for internal usage.
I got structure like this:
index.php
boot /
booter.php
application /
controllers /
indexcontroller.php
core /
template.class.php
model.class.php
controller.class.php
cache /
memcached.php
something /
something.php
Booter.php contains: (it's currently working only with files located in core directory):
class Booter
{
private static $controller_path, $model_path, $class_path;
public static function setDirs($controller_path = 'application/controllers', $model_path = 'application/models', $classes_path = 'core')
{
self::$controller_path = $controller_path;
self::$model_path = $model_path;
self::$class_path = $classes_path;
spl_autoload_register(array('Booter', 'LoadClass'));
if ( DEBUG )
Debugger::log('Setting dirs...');
}
protected static function LoadClass($className)
{
$className = strtolower($className);
if ( file_exists(DIR . '/' . self::$model_path . '/' . $className . '.php') )
{
require(DIR . '/' . self::$model_path . '/' . $className . '.php');
}
else if ( file_exists(DIR . '/' . self::$class_path . '/' . $className . '.class.php') )
{
require(DIR . '/' . self::$class_path . '/' . $className . '.class.php');
}
else if ( file_exists(DIR . '/' . self::$controller_path . '/' . $className . '.php') )
{
require(DIR . '/' . self::$controller_path . '/' . $className . '.php');
}
if ( DEBUG )
Debugger::log('AutoLoading classname: '.$className);
}
}
My application/controllers/indexcontroller looks like this:
<?
class IndexController extends Controller
{
public function ActionIndex()
{
$a = new Model; // It works
$a = new Controller; //It works too
}
}
?>
And here comes my questions:
[Question 1]
My code is working currently like this:
$a = new Model; // Class Model gets included from core/model.class.php
How I can implement including files by classes with namespaces? For example:
$a = new Cache\Memcached; // I would like to include file from /core/CACHE/Memcached.php
$a = new AnotherNS\smth; // i would like to include file from /core/AnotherNS/smth.php
and so on. How I can produce the handling of the namespace?
[Question 2]
Is it a good practice to use single autoload for Classes, Controllers and models or I should define 3 different spl_autoload_register with 3 different methods and why?
I normally have a bootstrap.php file inside a folder conf that is in the application root. My code is normally located inside an src folder, also located in the root, so, this works fine for me:
<?php
define('APP_ROOT', dirname(__DIR__) . DIRECTORY_SEPARATOR);
set_include_path(
implode(PATH_SEPARATOR,
array_unique(
array_merge(
array(
APP_ROOT . 'src',
APP_ROOT . 'test'
),
explode(PATH_SEPARATOR, get_include_path())
)
)
)
);
spl_autoload_register(function ($class) {
$file = sprintf("%s.php", str_replace('\\', DIRECTORY_SEPARATOR, $class));
if (($classPath = stream_resolve_include_path($file)) != false) {
require $classPath;
}
}, true);
You can generalize this into your "Booter" class and append directories to the include path. If you have well defined namespace names, there will be no problems with colisions.
Edit:
This works if you follow PSR-1.
Question 1:
In your autoloader, change the \ (for the namespace) into DIRECTORY_SEPARATOR. This should work:
protected static function LoadClass($className)
{
$className = strtolower($className);
$className = str_replace('\\', DIRECTORY_SEPARATOR, $className);
...
}
Always use DIRECTORY_SEPARATOR, especially if the software has the potential to be used on other platforms.
Question 2:
I would use one and separate your classes by namespace. However, I think that comes down to how you want to structure your framework and how you separate your code. Someone else may be able to better answer that question.

is_callable loads 'pcu2phmmr6pam3' via __autoload

I've got a weird problem going on. I'm developing my website localhost on a xampp server before uploading it to my host server. I finally finished the basic components of my website, so that it could load the homepage. Everything worked as planned.
So I decided to upload it to the host server, then I set the MySQL credentials, now it should work. Ehm.. no, it didn't. Empty page.
So I put on my gloves and started digging into the code with random expressions as
echo 'test'; so I could keep track of what was going on. It seems to run just fine until is_callable() was executed.
is_callable() should run __autoload($class), so I tried var_dump($class)
It gave me this result:
string(11) "Initializer"
string(8) "Database"
string(14) "PageController"
string(14) "BaseController"
string(14) "pcu2phmmr6pam3"
string(14) "pcu2phmmr6pam3"
Now, every class listed here should be there, besides the last two. I have NO idea where that name came from, because it's not a string I've set anywhere.
The only result Google showed me for pcu2phmmr6pam3 was another website having a similar problem.
Now the really weird stuff happens, my autoload function looks like this:
function __autoload($class) {
var_dump($class);
if (file_exists(ROOT_PATH . DS . 'site' . DS . 'class' . DS . $class . '.class.php')) {
require_once(ROOT_PATH . DS . 'site' . DS . 'class' . DS . $class . '.class.php');
} else if (file_exists(ROOT_PATH . DS . 'site' . DS . 'controller' . DS . $class . '.class.php')) {
require_once(ROOT_PATH . DS . 'site' . DS . 'controller' . DS . $class . '.class.php');
} else if (file_exists(ROOT_PATH . DS . 'site' . DS . 'model' . DS . $class . '.class.php')) {
require_once(ROOT_PATH . DS . 'site' . DS . 'model' . DS . $class . '.class.php');
} else{
throw new Exception('Class `' . $class . '` could not be loaded!');
}
}
Every class it should load, is loaded. If I want to create a class which doesn't exist, then it throws an exception.
But with the pcu2phmmr6pam3 'classes' neither is the case. No exceptions were thrown, and now errors were printed on the screen, even though I've set error_reporting(E_ALL)
Here's the surrounding code of is_callable():
$controllerName = ucfirst($this->structure) . 'Controller';
$action = strtolower(((!empty($this->uri[1]))?$this->uri[1]:'index'));
if (is_callable(array($controllerName, $action))) {
$controller = new $controllerName($this->uri, $this->database, $this->structure, $action, $page);
$controller->$action();
} else if (is_callable(array($controllerName, 'index'))) {
$controller = new $controllerName($this->uri, $this->database, $this->structure, 'index', $page);
$controller->index();
} else {
$controller = new NotfoundController($this->uri, $this->database, 'Notfound', 'index', $page);
$controller->index();
}
Last bit of information I can give you:
My localhost xampp server runs PHP 5.4.7, and my host server runs PHP 5.3.20.
Solved, not sure how the weird classnames appeared, if someone knows, I'd like to know why :)
I had a similar problem calling a static method. (same with spl_autoload_register())
function __autoload ($className) {
echo $className."\n";
}
class ClassName {
function functionName () {
}
}
//calls __autoload twice with class name 'a22h1pd_t'
is_callable( array('ClassName', 'functionName') );
//doesn't call __autoload
is_callable('ClassName::functionName');
Looking at this minimal example it's obvious. The static keyword for functionName is missing. Adding it resolved the problem for me with both syntaxes behaving as expected.

Categories