autoloader when executing php from linux bash - php

im currently working on some sort of upload with automatic video conversion. At the moment i am executing a php script via php shell command after the upload is finished so the user doesn't have to wait until the conversion is completed. Like so:
protected function _runConversionScript() {
if (!exec("php -f '" . $this->_conversionScript . "' > /dev/null &"))
return true;
return false;
}
Now in my conversion script file i am using functions from another class "UploadFunctions" to update the status in the database (like started, converted, finished...). The problem there is though that this UploadFunctions class inherits from another class "Controller" where for example the database connection gets established. Currently i am using spl_autoloader to search specific directories for the files needed (for example controller.php), but because the conversion script is out of context with the whole autoloader stuff it doesn't recognize the Controller class and throws an fatal php error.
Here is some code from the conversion script:
require_once('uploadfunctions.php');
$upload_func = new UploadFunctions();
// we want to make sure we only process videos that haven't already
// been or are being processed
$where = array(
'status' => 'queued'
);
$videos = $upload_func->getVideos($where);
foreach ($videos as $video) {
// update database to show that these videos are being processed
$update = array(
'id' => $video['id'],
'status' => 'started'
);
// execute update
$upload_func->updateVideo($update);
.........
Am i doing this completly wrong or is there a better way to accomplish this? If you need more code or information please let me know!
Thanks a lot
Here is my spl_autoload code:
<?php
spl_autoload_register('autoloader');
function autoloader($class_name) {
$class_name = strtolower($class_name);
$pos = strpos($class_name ,'twig');
if($pos !== false){
return false;
}
$possibilities = array(
'..'.DIRECTORY_SEPARATOR.'globals'.DIRECTORY_SEPARATOR.$class_name.'.php',
'controller'.DIRECTORY_SEPARATOR.$class_name.'.php',
'..'.DIRECTORY_SEPARATOR.'libs'.DIRECTORY_SEPARATOR.$class_name.'.php',
'local'.DIRECTORY_SEPARATOR.$class_name.'.php'
);
foreach ($possibilities as $file) {
if(class_exists($class_name) != true) {
if (file_exists($file)) {
include_once($file);
}
}
}
}
?>
I have my project divided into subfolders wich represent the functionality, for example upload, myaccount and gallery.. in every subfolder there are also 2 other folders: controller and local. Controller is the class controlling this part (upload for example) and local is the folder where i am putting the local classes wich are needed. The controller class gets called from the index.php wich is located in the sub-project folder. "libs" and "global" are just projectwide classes, like database, user and so on.
This is an example of my folder structure:
www/index.php // main site
www/upload/index.php // calls the controller for upload and initializes the spl_autoload
www/upload/controller/indexcontroller.php // functionality for the upload
www/upload/local/processVideo.php // this is the conversion script.
I am fairly new to spl_autoload function. In my opinion the spl_autoload is not getting called if my script is calling: "php -f processVideo.php", isn't it?

PHP relative paths are calculated from the path where PHP binary is called.
I suggest you to use __DIR__ constant to avoid that behavior
http://php.net/manual/en/language.constants.predefined.php

I was actually able to resolve the issue. I had to include the spl_autoload_register function inside the conversion script so that it was able to locate the files. This was an issue because the conversion script is not build into my framework an so it isn't able to load the classes from the framework autoloader.

Related

How to call same name dynamic function from dynamically created files based database inputs

I'm creating a system that would work on call same function names from dynamic created php files.
The file structure is as bellow :
/root/caller.php //{uses pthreads & process multi simultaniously}
/root/rules/mode1.php
/root/rules/mode2.php
/root/rules/mode{n}.php // goes till n , so unlimited
Caller.php reads data from db and calls mode{n} accordingly. Sometimes there are chances to call multiple of rules at a point of time.
Sample of caller.php {written on OOPS}
foreach ($result as $row){
$mode = $rom->modeNum;
include 'rules/mode' . $mode . '.php';
call_fuction_inside_mode();
}
Sample of mode{n}.php {procedural programming}
function call_fuction_inside_mode(){
//..Custom function depending upon mode{n}
}
This won't function properly, because caller.php would include same function name again and again.
Please note :
I do not want to play with rename function with pecl, as all includes would be required
I have tried calling mode{n}.php by introducing another file "proxy.php"
And proxy.php works in below pattern
caller.php -> curl 127.0.0.1/proxy.php?mode={n} -> calls as defined
But this is not efficient way to do this when dealing with >500 requests per sec on micro server. I noticed many requests are killed and there's problem with order number. My target is to achieve this with least memory usage and 100% utilization of provided cpu resources.
I'm confused and so curious to find the solution I should be dealing here.
create an interface called Mode in Mode.php:
interface Mode{
public static function AcivateMode();
}
and include Mode.php into your code, then change the code in your mode{n}.php to encapsulate your function into classes that implements the interface Mode like so:
class Mode1 implements Mode {
public static function AcivateMode() {
//..Custom function depending upon mode{n}
}
}
then modify your foreach to activate the modes like:
foreach ($result as $row){
$mode = $rom->modeNum;
include 'rules/mode' . $mode . '.php';
call_user_func("Mode$mode::AcivateMode");
}
OR
you can use the namespaces approach by adding the namespace to each of your mode{n}.php files like so:
mode1.php
namespace Mode1;
function call_fuction_inside_mode(){....}
and calling the function using:
call_user_func("Mode$mode\call_fuction_inside_mode");
that worked for me, hope it's gonna be helpful :)
Regards,

Is there any way to instantiate CodeIgniter and call a function inside a controller from an outside class?

I am creating a cron job that will run every few minutes and it will read from database data to see which function needs to be called and act accordingly to the data but half the crons are written in codeigniter and the other half in native php.
Is there any way to do this? I have been googling but the anwser. I came up with is that it is impossible. I tried changing directory and than including or requiring index.php from the codeigniter, in which the function is that i need to call.
while doing this if my class is written in native php, It returns some errors that don't make sense and if I correct those errors, I would say that half the system function from codeigniter would be gone. It would still be a question if it will work even then.
If my class is written in codeigniter, when I include index.php, it just breaks. No errors no response or it says that the "ENVIRONMENT" variables are already defined. Thus I have been looking for a way to undefine those variables from config file or overwrite them to null or empty string but nothing works.
If you have any ideas it would be much appreciated.
I saw a question question link about some cron jobs in php where the user #michal kralik gave an answer about what i am doing in general with database and one cron class that will call other crons (coould use help for that too).
btw forgot to mention that using curl and exec will not work, because on our servers they sometimes just stop working for no reason.
UPDATE 1:
this is my class currently after so many tries:
class Unicron extends MY_Controller {
public $config;
function __construct() {
parent::__construct();
}
public function init(){
$config['base_url'] = "http://localhost/test";
define('EXT_CALL', true); // Added EXT_CALL constant to mark external calls
$_GET['controller/method'] = ''; // add pair to $_GET with call route as key
$current = getcwd(); // Save current directory path
chdir('C:/inetpub/wwwroot/test/'); // change directory to CI_INSTALLATION
include_once 'index.php'; // Add index.php (CI) to script
define('BASEPATH', 'C:/inetpub/wwwroot/test/system/');
$this->load->library("../../application/controllers/controller.php");
$job = new $this->controller->Class();
$isDone = $job->exportExcel(somekey);
echo $isDone;
$CI =& get_instance(); // Get instance of CI
$CI->exportExcel('baseparts', 'exportExcel');
// FOR STATIC CALLING!!
//$CI->method('controller','method');
//replace controller and method with call route
// eg: $CI->list('welcome','list'); If calling welcome/list route.
//$OUT->_display(); // to display output. (quick one)
// Or if you need output in variable,
//$output = $CI->load->view('VIEW_NAME',array(),TRUE);
//To call any specific view file (bit slow)
// You can pass variables in array. View file will pick those as it works in CI
chdir($current); // Change back to current directory
echo $current;
}
where i try to define BASEPATH it does not define it nor override the previous value.
In the index.php of other codeigniter i put:
if(!defined('ENVIRONMENT'))
define('ENVIRONMENT', 'development');
this way i resolved my issue with ENVIRONMENT already being set error.
this is a few things i found and combined together, hoping it could work but still when i call it via command line it shows nothing (even tried echo anything everywhere and nothing).
This may be a long comment rather than a answer as the code supplied requires a lot of work to make it useful.
Running multiple instances of 'codeigniter' - executed from codeigniter.
Using the 'execute programs via the shell' from PHP. Each instance runs in its own environment.
There are some excellent answers already available:
By default the PHP commands 'shell' wait for the command to complete...
732832/php-exec-vs-system-vs-passthru.
However, we want to 'fire and forget' quite often so this answer is quite useful...
1019867/is-there-a-way-to-use-shell-exec-without-waiting-for-the-command-to-complete
All i did was use this show an example of how to use 'codeigniter' to do this. The example was the 'Hello World' cli example from user manual. The version of ci is 2.1.14. I haven't used 'ci' before.
It is tested and works on 'PHP 5.3.18' on windows xp.
As well as the usual 'Hello World' example, i used an example of a a command that uses 'sleep' for a total of 20 seconds so that we can easily see that the 'ci' instances are separate from each other while executing.
Examples:
<?php
class Tools extends CI_Controller {
// the usual 'hello world' program
public function message($to = 'World')
{
echo "Hello {$to}!".PHP_EOL;
}
// so you can see that the processes are independant and 'standalone'
// run for 20 seconds and show progress every second.
public function waitMessage($to = 'World')
{
$countDown = 20;
while ($countDown >= 0) {
echo "Hello {$to}! - ending in {$countDown} seconds".PHP_EOL;
sleep(1);
$countDown--;
}
}
}
'ci' code to run 'ci' code...
<?php
class Runtools extends CI_Controller {
/*
* Executing background processes from PHP on Windows
* http://www.somacon.com/p395.php
*/
// spawn a process and do not wait for it to complete
public function runci_nowait($controller, $method, $param)
{
$runit = "php index.php {$controller} {$method} {$param}" ;
pclose(popen("start \"{$controller} {$method}\" {$runit}", "r"));
return;
}
// spawn a process and wait for the output.
public function runci_wait($controller, $method, $param)
{
$runit = "php index.php {$controller} {$method} {$param}";
$output = exec("{$runit}");
echo $output;
}
}
How to run them from the cli...
To run the 'ci' 'nowait' routine then do:
php index.php runtools runci_nowait <controller> <method> <param>
where the parameters are the ci controller you want to run. Chnge to 'runci_wait' for the other one.
'Hello World: 'wait for output' - (ci: tools message )
codeigniter>php index.php runtools runci_wait tools message ryan3
Hello ryan3!
The waitMessage - 'do not wait for output' - (ci : tools waitMessage )
codeigniter>php index.php runtools runci_nowait tools waitMessage ryan1
codeigniter>php index.php runtools runci_nowait tools waitMessage ryan2
These will start and run two separate 'ci' processes.

Yii console with different configs

In the web application we use same code and modules with different configs like:
in index.php file app will decide, wchin config to turn:
switch($_SERVER['HTTP_HOST']){
default:
$yii=$webRoot.'/framework/yiilite.php';
$config = $webRoot.'/protected/config/main.php';
break;
case 'someurl.com':
...
break;
...
}
But, how can I do it with console application?
The reason in that I use different databases ant etc.
is it possible to do something like this:
$ ./protected/yiic --application=myappname [all defined commands as default]
in the code a
--application
will set with which console config to work
more explanation
my answer to #Joe Miller
But the problem is, how choose theme?
I did in the files foloowings:
in protectes/yiic
$__appId = null;
for( $__i=1,$__max=count($argv); $__i<$__max; ++$__i ) {
if ( strpos($argv[$__i],'--appid',0) === 0 ) {
$__appId = substr($argv[$__i], 8);
unset($argv[$__i]);
}
}
require_once(dirname(__FILE__).'/yiic.php');
and in protected/yiic.php
$__appIdsList = array(
'my_site_1',
'my_site_2',
'my_site_3',
'my_site_4',
);
$yiic=dirname(__FILE__).'/../framework/yiic.php';
$config=dirname(__FILE__).'/config/console_'.$__appId.'.php';
require_once($yiic);
and it works and it catchs that config file what I need
./protected/yiic --appid=my_site_1
bu when I`m trying to do migrate
./protected/yiic --appid=my_site_1 migrate
the app cant recognize comman and gives me migrates help list
And final conslusion (I solved it)
I`d like to add transperent console command without affecting it to other execution of builtin console commands and custom console commands.
Another requirement is, solve this issue on a low-level approach, without inheritance or overloading other classes or methods.
So, my solution is:
in protected/yiic
#!/usr/bin/env php
<?php
$__appId = null;
for( $__i=1,$__max=count($argv); $__i<$__max; ++$__i ) {
if ( strpos($argv[$__i],'--appid',0) === 0 ) {
$__appId = substr($argv[$__i], 8);
unset($argv[$__i]);
unset($_SERVER['argv'][$__i]);
$argv = $_SERVER['argv'] = array_values($argv);
}
}
require_once(dirname(__FILE__).'/yiic.php');
and in /protected/yiic.php
<?php
// change the following paths if necessary
$__appIdsList = array(
'app_1',
'app_2',
);
$yiic=dirname(__FILE__).'/../framework/yiic.php';
$config=dirname(__FILE__).'/config/console_'.$__appId.'.php';
if ( !is_file($config) ) {
die("Error: There is no or wrong parametr appid. Please set parametr or correct. Example -appid={application_name}\n\tThe list of available appid:\n\t\t - ".implode("\n\t\t - ", $__appIdsList));
}
require_once($yiic);
and now it is possible to set param "appid" in any place of command line, like
./protected/yiic migrate --appid=app_1
and it acts only in that app what we need
PS: in any case, thanks #Joe Miller
Copy yiic.php for example to cron.php and modify the config file in the cron.php
then use as if it were yiic, for example:
cd ~/protected;php ~/protected/cron.php app command --param=value >> ~/runtime/crontab.log
If I've understood what you're trying to do correctly, I think you might need something like this. I've referred to this article http://www.yiiframework.com/doc/guide/1.1/en/topics.console#creating-commands. I've not tried this, so I'm just interpreting the article.
Create a base command class, from which you will extend all the other commands. The base class run() method selects the config file to load.
In protected>commands you need a file migrate.php. This must contain the class MigrateCommand, and must extend CConsoleCommand. You can then override the run() method of this class to allow parameters to be passed to the method. e.g.
In protected>commands>baseCommand.php
class MyBaseCommand extends CConsoleCommand{
public function run($args){
//Code here to select the config file to load
//$args are any arguments you have passed in the command line
}
}
In protected>commands>migrate.php
class Migrate extends MyBaseCommand{
public function run($args){
parent::run($args);
//Do your own stuff here
}
}
you should then be able to call the command as;
./protected/yiic migrate --appid=my_site_1
Note that the name of the command appears first, I'm not sure if this is important, but it's what the guide says! I hope I've understood your question this time!
I think, I founded more confortable solution!
It`s more easy, and solved all my requirements.
in the file
protected/yiic.php
I write:
...
$yiic=dirname(__FILE__).'/../lib/framework/yiic.php';
if ( strpos(__FILE__,{first/place}) !== false ) {
$config=dirname(__FILE__).'/config/first_config.php';
} elseif ( strpos(__FILE__,{second/place}) !== false ) {
$config=dirname(__FILE__).'/config/second_plase.php';
} else {
// by default
$config=dirname(__FILE__).'/config/console.php';
}
require_once($yiic);
...
where {first/place},{second/place} - a part of the project`s path. For example:
Your first project is placed in:
/var/www/aproject/first_one
and the second one on the
/var/www/aproject/second_one
than you checks will be:
// for first porject
strpos(__FILE__,'aproject/first_one') !== false
and etc.

PHP: spl_autoload_register + RecursiveDirectoryIterator

I want to loop all the sub dirs in the main dirs, where I keeps all my classes, for instance,
core/
model/
page/
class_1.php
class_2.php
menu/
class_3.php
and so on...
So this is my autoload function that I place it in the init.php,
function autoload_multiple_directory($class_name){
// List all the class directories in the array.
$array_directories = array(
'core/controller/',
'core/model/',
'core/helper/'
);
// When you use namespace in a class, you get something like this when you auto load that class \foo\tidy.
// So use explode to split the string and then get the last item in the exloded array.
$parts = explode('\\', $class_name);
//print_r($parts);
// Set the class file name.
$file_name = strtolower(end($parts)).'.php';
// $file_name = 'class_'.strtolower($class_name).'.php';
// Loop the array.
foreach($array_directories as $path_directory){
$recursive_directory = new RecursiveDirectoryIterator($path_directory);
foreach (new RecursiveIteratorIterator($recursive_directory) as $filename => $file) {
if(file_exists(WEBSITE_DOCROOT.$file->getPath().'/'.$file_name)){
include WEBSITE_DOCROOT.$file->getPath().'/'.$file_name;
}
}
/* no problem with this, but I cannot loop the sub dirs...
if(file_exists(WEBSITE_DOCROOT.$path_directory.$file_name)){
include WEBSITE_DOCROOT.$path_directory.$file_name;
}
*
*/
}
}
spl_autoload_register('autoload_multiple_directory');
But then I get this error message,
Fatal error: Cannot redeclare class Common in C:\wamp\www\xxx\core\helper\Common.php on line 6
There is only one Common class in my project. Why does it say more than once or redeclare?
But if you look at the if(file_exists(WEBSITE_DOCROOT.$path_directory.$file_name)) that I comment out - it has no problem to load the classes. The problem of this initial loop is that it does not loop the sub dirs in a main dir, for instance, core/model/
Any ideas why and what should I do to loop the sub dirs of a main dir?
EDIT:
The problem comes from RecursiveDirectoryIterator - it loops the directories and lists all files. But what I want is only the sub directories.
Is there any chance that there are more than one copy of Common.php file exists in those folders?
Because your code does not break after including a class file the autoloader will continue seeking other files with the same name in the folder tree, and would lead to Fatal error: Cannot redeclare class XXX error. Adding break could fix the problem.
// Loop the array.
$isClassFound = false;
foreach($array_directories as $path_directory){
$recursive_directory = new RecursiveDirectoryIterator($path_directory);
foreach (new RecursiveIteratorIterator($recursive_directory) as $filename => $file) {
if(file_exists(WEBSITE_DOCROOT.$file->getPath().'/'.$file_name)){
include WEBSITE_DOCROOT.$file->getPath().'/'.$file_name;
$isClassFound = true;
}
if ($isClassFound) break;
}
if ($isClassFound) break;
}
However, the nature of your autoloader seems like it should not allow any duplicated class file names. Maybe you can write a name duplication checker to guarantee uniqueness.
EDIT:
I removed the class_exists() part from my answer because using it doesn't make much sense. Anyway, since you saw that version of my answer, and you asked me where to put class_exists() via the comment, I'll revive the code sample. you can add the following code at the beginning of the autoloader.
if (class_exists($class_name,false)) // give false to avoid automatic loading
return;
Open the start menu. In the text box write cmd, wait for the cmd program to pop up and then hit Enter.
Once the terminal window opens navigate to your root folder and then do a recursive search (through all files and folders) for the class' name:
cd C:\wamp\www\xxx
findstr /SNIP /C:"class common" *.php
There should be more than one declaration of the class.

Path issue with autoload function to get files for use in Ajax

So my project uses an MVC framework and I have a page with an Ajax script I run to get content from the server. When the PHP script is called in the Ajax script, I want to access the classes already in my library for use in the PHP script. To do this, I use what I call an ajaxBootstrap to call the appropriate function that then instantiates the objects needed for that specific Ajax script.
To load those classes from my library I have an autoload function in my ajaxBootstrap so I don't need to use a bunch of require and include statements. My problem is those files aren't being loaded due to a path issue with the autoload function. When I use a require statement with the same path, the classes load with no problems, its only when I try to load them using the autoload function that I get an 500 internal server error.
Here is my ajaxBootstrap file:
// This file routes Ajax requests made in JS files and instantiates a specific object to carry out the actions needed for that particular Ajax operation
// Autoload any classes that are required
function autoLoad($classToLoad)
{
if(file_exists('../library/' . $classToLoad . 'class.php')) // File in the library folder
{
require('../library/' . $classToLoad . '.class.php');
}
else if(file_exists('../../app/models/' . $classToLoad . 'class.php')) // File in the models folder
{
require('../../app/models/' . $classToLoad . '.class.php');
}
}
spl_autoload_register('autoLoad');
// Determine which function to call based on the url that's listed in the Ajax request
switch($_GET['action'])
{
case 'pageOne':
pageOne();
break;
case 'pageTwo':
pageTwo();
break;
}
function pageOne()
{
$test = new Test();
$test->funcThatReturnStuff();
}
function pageTwo()
{
$test2 = new Test2();
$test2->funcThatReturnStuff();
}
Like I mentioned eariler, if I use a require statement such as:
require('../library/Test.class.php');
$test = new Test();
$test->funcThatReturnStuff();
The class loads and works just fine. But using the same path in the autoloader function throws an error. The really odd thing is if I put an else if statement in the autoloader that loads a class from the folder where my ajaxBootstrap is it also works fine too...
I know I could just use the require statements and be done with the problem but I want to be able to scale the project and not need to use loads of require statements in the future. BTW, I use '../' to get from where my ajaxBootstrap file is to my other folders.
Also, to add to my previous post, I've tried replacing the ../ with absolute paths using define('ROOT', dirname(__FILE__) . '/') and also define('ROOT', $_SERVER['DOCUMENT_ROOT'] . '/path/to/folder/') neither of which worked and still gave me the internal server error in Firebug. In addition, I haven't received any errors in my error log either.
Nevermind... Even after staring at my code for the past few hours I somehow missed the missing period in two of my file paths. I hate coding sometimes... Thank you to anyone who took time to read this.

Categories