yiisoft/yii

getPathOfAlias('webroot') return wrong value in console applications at least with default settings

teo1978 opened this issue · 31 comments

yiic seems to be designed to be run from the "protected" directory, at least that's what all the tutorials suggest.

however, when you run a ConsoleApplication using yiic, at least without tweaking any of the default settings of a default installation, getPathOfAlias('webroot') will return the path of the protected directory, which usually not the webroot.

This seems to be because CApplication sets the path of webroot to dirname($_SERVER['SCRIPT_FILENAME'])
(https://github.com/yiisoft/yii/blob/1.1.12/framework/base/CApplication.php#L136) but that's the expected value only for web applications.

Note an issue since 'webroot' is not well defined by nature when you run the app in console mode.

Hmmm, I guess you're right from a phylosophical point of view. However, if one writes a command script to automate a series of actions usually performed from the web, and hence call code that was written for the web, (isn't that quite common?), then that will break a lot of code. Then probably a good solution would be to offer a way to easily configure the path of webroot via the application's config - or is that already possible?

I mean, if it is not well defined by nature, then it means it should be possible and easy to define it artificially.

cebe commented

@matteosistisette you can set Yii::setPathOfAlias('webroot', ...) to your desired path when in console scope e.g. in console config file.

whopssss I forgot I could execute statements before returning the config array :$:$:$

Hey, this needs to be reopened!
@cebe 's suggestion DOES NOT work because it cannot be done in config/console.php file, because that is run BEFORE the framework's code calls base/CApplication.php: Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));

So, whatever value you set within config/console.php, is then overwritten.
The only way to change the default value of the path is to call setPathOfAlias after the application is created but before it is run. Which right now means changing the very framework's yiic.php. Not the one in protected, but the one in framework, because that calls run().

A way must be provided to override the default "webroot" alias without having to modify framework code.

You can do so in index.php, for example:

$config = require '...';
$app = Yii::createWebApplication($config);
Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
$app->run();
cebe commented

@matteosistisette you can put your setPathOfAlias statement into your application preinit() method to overide the default one.

@samdark it's yiic.php not index.php. It includes the framework's yiic.php which calls both createapplication and run

$config = require '...';
$app = Yii::createWebApplication($config);
chdir('../'); //going to webroot
$app->run();

works like magic

@AnatolyRugalev it's a ConsoleApplication not a WebApplication and I'm afraid of the side effects of chdir'ing. Anyway I thing @cebe's preinit() suggestion is the solution indeed.

However wouldn't it be more elegant to allow configuring it in the config array the same way it can be done with basePath?? like in https://github.com/yiisoft/yii/blob/1.1.13/framework/base/CApplication.php#L135

A console app could be for 5—6 webroots or w/o webroots at all. I think it's too application specific.

cebe commented

The thing I found weird here is that CApplication defines the alias and not CWebApplication. But we can not change this as of BC.

Hey but how am I supposed to override preinit() if yiic.php hardcodedly instantiates a CConsoleApplication ??

@matteosistisette, sorry, I copied @samdark's code. This works perfect for me

cebe commented

@matteosistisette you can also have your own yiic.php by just copying over from the framework if you have special needs.

Ok. I thought I should strive to avoid that, it seems a pretty dirty hack

Also I wonder where I would have to place my extended ConsoleApplication class, as I don't think it will find it in the "components" folder at that stage of code initialization...

In the end this is the old problem of "how do I cleanly extend CConsoleApplication or CWebApplication"

cebe commented

there is no problem with putting it in components folder and also overriding yiic.php is not dirty.

I don't see a problem here as well. I rarely use built-in webroot alias and i always use my own alias (which can be called something like www, media, htdocs, etc.).

@resurtm that's not always an option. Sometimes you need the same code to be run both from web and console application. Anyway, the solution is extending CConsoleApplication (you need to explicitly include the file too) and copying and modifying framework/yiic.php in order to use that. I still think having to "rewrite" the whole core yiic.php file just to change the name of the class being instantiated is too much, though. (e.g. the name of the application class should be allowed to be defined in the config file, or in a constant or whatever)

@matteosistisette here's the solution w/o overriding the app:

yiic.php:

defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('YII_DEBUG') or define('YII_DEBUG',true);
require_once('path_to_framework'.'/yii.php');
$config = require 'path_to_config.php';
$app=Yii::createConsoleApplication($config);

$app->commandRunner->addCommands(YII_PATH.'/cli/commands');
$env=@getenv('YII_CONSOLE_COMMANDS');
if(!empty($env))
   $app->commandRunner->addCommands($env);

Yii::setPathOfAlias('webroot','path_to_your_webroot');

$app->run();
cebe commented

I still think having to "rewrite" the whole core yiic.php file just to change the name of the class being instantiated is too much

yiic.php in the framework is an example of how to create a console application. If you have special needs you can copy that example and adjust it.

Just to clarify I'm talking about the one that is inside the framework directory, not the one in the protected directory which include()s it.

@matteosistisette:

@resurtm that's not always an option. Sometimes you need the same code to be run both from web and console application.

I'm defining all custom aliases both in console.php and main.php configs and i use the same code which deals with aliases both from console and web applications. This is not an issue.

@resurtum of course if you plan it in advance. If you have already written a webapp that uses the webroot alias in a thousand places and then decide to create a console app that will do some job using the same models, you would have to do a search and replace.

cebe commented

Just to clarify I'm talking about the one that is inside the framework directory, not the one in the protected directory which include()s it.

I know, I was talking about the file in the framework dir. It is included by the one in the application as it uses same functionality in simple cases. You can have your own console app in a yiic.php but you can also name your console app file like you want. Use the framework yiic as a template but no need to include it at all.

In my case

Yii::setPathOfAlias('webroot',getcwd().'/protected');

dtmp commented

if (get_class(Yii::app()) == 'CConsoleApplication') {
$root_path = preg_replace('//protected$/', '', Yii::getPathOfAlias('webroot'))
}

is there any solution?