I'm using two twig template paths found in this question How do I specify two locations for Twig templates?
$template = isset($config->template) ? $config->template : 'default';
$templates_dir = $root . 'templates' . DIRECTORY_SEPARATOR;
if (isset($config->template) && $config->template != 'default') {
$template = array($templates_dir . $config->template, $templates_dir . 'default');
} else {
$template = $templates_dir . 'default';
}
$loader = new Twig_Loader_Filesystem($template);
and I have code like this:
function render($request, $page, $data = array()) {
global $twig, $config;
$template = isset($config->template) ? $config->template : 'default';
$base = baseURI($request);
return $twig->render($page, array_merge(array(
"path" => $base . '/templates/' . $template,
"root" => $base
), $data));
}
and what I need is to get path of the template so I can use it as 'path' variable (so I can use current path for assets like "{{path}}/img/foo.png").
By searching source code I found solution in one of the test file:
$loader->getSourceContext($page)->getPath();
so if you want to have a path you need to remove template name:
$page = 'admin.html';
$path = $loader->getSourceContext($page)->getPath();
$path = preg_replace('%/'.$page.'$%', '', $path);
and if you want to get relative path you can use:
preg_replace("%" . __DIR__ . "%", "", $path);
You can wrap this in a function:
function template_path($page) {
global $loader;
$path = preg_replace("%" . __DIR__ . "%", "", $loader->getSourceContext($page)->getPath());
return preg_replace('%/'.$page.'$%', '', $path);
}
Related
zipping some directory on the server I receive extra folder inside zip-archive under Windows and Linux.
Extra folder "3b84" is the ending of using md5(integer) function to set filename inside getExportPaths() method. If I try to make filename shorter - it makes me a zip archive where all files placed in one directory without their subdirectories like "CSS", "fonts" etc.
And one important thing why I'm using md5() - I need to paste into one directory a lot of zip archives. So their name should be unique.
How to fix this and remove such extra folder with a name like "3b84"?
public function actionExportdocument($id)
{
$model = Order::findOne(['id' => $id, 'user_id' => Yii::$app->user->getId()]);
if (!$model) {
throw new BadRequestHttpException('Export not found.');
}
$paths = Order::getExportPaths($model->id, $model->design->path);
$prepareFiles = Order::prepareFiles($paths, $model);
$zip = new ZipArchive();
$zip->open($paths['exportRoot'] . DIRECTORY_SEPARATOR . $paths['exportFile'],
ZipArchive::CREATE | ZipArchive::OVERWRITE);
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($paths['exportPath']),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file)
{
// Skip directories (they would be added automatically)
if (!$file->isDir())
{
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($paths['designPath']) + 1);
$zip->addFile($filePath, $relativePath);
}
}
$zip->close();
FileHelper::sendFile($paths['exportRoot'] . DIRECTORY_SEPARATOR . $paths['exportFile'], 'script.zip');
}
Order class contains:
public static function getExportPaths($modelId, $designPath)
{
$paths['orderIdHash'] = md5($modelId);
$paths['designPath'] = \Yii::getAlias('#app') . DIRECTORY_SEPARATOR . 'designs' . DIRECTORY_SEPARATOR . $designPath;
$paths['exportRoot'] = \Yii::getAlias('#webroot') . DIRECTORY_SEPARATOR . 'export-document';
$paths['exportPath'] = $paths['exportRoot'] . DIRECTORY_SEPARATOR . $paths['orderIdHash'];
$paths['exportFile'] = $paths['orderIdHash'] . '.zip';
return $paths;
}
public static function prepareFiles($paths, $model)
{
$exportDir = \Yii::getAlias('#webroot') . DIRECTORY_SEPARATOR . 'export-document';
if (!is_dir($exportDir)) {
mkdir($exportDir);
}
FileHelper::deleteDirectory($paths['exportPath']);
FileHelper::copyRecursive($paths['designPath'], $paths['exportPath']);
$content = '';
foreach ($model->orderTemplates as $template) {
$content .= $template->value;
}
$str = file_get_contents($paths['exportPath'] . DIRECTORY_SEPARATOR . 'index.html');
$str = str_replace("{{CONTENT}}", $content, $str);
file_put_contents($paths['exportPath'] . DIRECTORY_SEPARATOR . 'index.html', $str);
}
If I've read your question correctly; you wish to remove folders like /3b84/ from being generated.
Look inside your Order class:
public static function getExportPaths($modelId, $designPath)
{
$paths['orderIdHash'] = md5($modelId);
$paths['designPath'] = \Yii::getAlias('#app') . DIRECTORY_SEPARATOR . 'designs' . DIRECTORY_SEPARATOR . $designPath;
$paths['exportRoot'] = \Yii::getAlias('#webroot') . DIRECTORY_SEPARATOR . 'export-document';
$paths['exportPath'] = $paths['exportRoot'] . DIRECTORY_SEPARATOR . $paths['orderIdHash'];
$paths['exportFile'] = $paths['orderIdHash'] . '.zip';
return $paths;
}
See $paths['exportPath'] = $paths['exportRoot'] . DIRECTORY_SEPARATOR . $paths['orderIdHash']; ?
Remove that and you your new path will no longer have folders like /3b84/.
The problem was that I've specified wrong $relativePath.
This string was incorrect
$relativePath = substr($filePath, strlen($paths['designPath']) + 1);
And instead of it
$relativePath = substr($filePath, strlen($paths['exportPath']) + 1);
I am setting file include path dynamically based on URI segments as below
URI is something like
http://script/profile/
code
$segments = explode( '/', $uri );
$file = end( $segments ) . '.php';
foreach ( $segments as $segment ) {
$dirs[] = $segment;
}
$include_path = rt_trailingslash_dir( root()->template_dir ) . implode( DIRECTORY_SEPARATOR, $dirs );
print_r($include_path . '/' . $file);
Output Something like this
c:/xampp/htdocs/.../template\profile\profile.php
Using that path in include
include $include_path . DIRECTORY_SEPARATOR . $file;
Output
template\profile.php
Which is wrong. It should be rather
template\profile\profile.php
I don't know what wrong I am doing..?
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.
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.
I've a project like this:
Now i want to autoload all the php files in the folder classes and sub folders.
I can do that with this:
$dirs = array(
CMS_ROOT.'/classes',
CMS_ROOT.'/classes/layout',
CMS_ROOT.'/classes/layout/pages'
);
foreach( $array as $dir) {
foreach ( glob( $dir."/*.php" ) as $filename ) {
require_once $filename;
}
}
But i dont like this. For example.
"layout/pages/a.php" extends "layout/pages/b.php"
Now i get an error because a.php was loaded first. How do you people load your project files? Classes?
SOLVED :)
This is my code now:
spl_autoload_register('autoloader');
function autoloader($className) {
$className = str_replace('cms_', '', $className);
$className = str_replace('_', '/', $className);
$file = CLASSES.'/'.$className.'.php';
if( file_exists( $file ) ) {
require_once $file;
}
}
You should try this
<?php
spl_autoload_register('your_autoloader');
function your_autoloader($classname) {
static $dirs = array(
CMS_ROOT.'/classes',
CMS_ROOT.'/classes/layout',
CMS_ROOT.'/classes/layout/pages'
);
foreach ($dirs as $dir) {
if (file_exists($dir . '/'. $classname. '.php')) {
include_once $dir . '/' . $classname . '.php';
}
}
}
After registering your_autoloader with spl_autoload_register() it will be called by the php interpreter every time you access a class that:
Has not already been loaded with require_once() or include_once()
Is not part of the PHP internals