I am using Twig with Slim, and am getting the following error:
Warning: file_get_contents(application/templates/config.html): failed
to open stream: No such file or directory in
/var/www/testing/vendor/twig/twig/lib/Twig/Loader/Filesystem.php on
line 131
The script below is located in /var/www/testing/html/index.php, and I have verified that the template exists at /var/www/testing/application/templates/config.html.
$container['view'] = function ($c) {
$view = new \Slim\Views\Twig('../application/templates', [
//'cache' => 'path/to/cache' // See auto_reload option
'debug' => true,
'strict_variables'=> true
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$c['router'],
$c['request']->getUri()
));
$view->addExtension(new \Twig_Extension_Debug());
return $view;
};
$app->get('/config', function (Request $request, Response $response) {
return $this->view->render($response, 'config.html',[]);
});
Line 131 is shown below and returns config.html.
public function getSource($name)
{
return file_get_contents($this->findTemplate($name));
}
I have used this same script in another similar server (however, maybe different PHP version and php.ini and httpd.conf may be different), and do not have this issue?
Obviously, I have configured something incorrectly. How should I configure Twig to find templates?
Yes, that's a problem (bug?) I've encountered last week, and #geggleto solved it.
This is because of Twig update from v1.26.1 from 1.24.2.
This is how Twig grabbed in 1.24.2 (method Twig_Loader_Filesystem::normalizeName):
protected function normalizeName($name)
{
return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
}
And this is how it grabs file in 1.26.1:
private function normalizePath($path)
{
$parts = explode('/', str_replace('\\', '/', $path));
$isPhar = strpos($path, 'phar://') === 0;
$new = array();
foreach ($parts as $i => $part) {
if ('..' === $part) {
array_pop($new);
} elseif ('.' !== $part && ('' !== $part || 0 === $i || $isPhar && $i < 3)) {
$new[] = $part;
}
}
return implode('/', $new);
}
See that array_pop($new); line? That's the one that ruins use of relative path.
#geggleto suggested to use absolute path instead of relative, and it worked:
\Slim\Views\Twig(__DIR__.'/../application/templates')
To sum up: this happens because of Twig new version.
Related
I'm trying to make my project multi-language.
I want to work with JSON files since my project has lots of pages,so a lot of strings to translate.
In order to avoid confusion I want to use nested JSON objects instead of just keys.
For example my en.json file looking like this:
{
"login": {
"login": "Login",
"email": "Email",
"emailPlaceholder": "Registered email address",
"password": "Password",
"passwordPlaceHolder": "Your account password"
},
"dashboard": {}
}
So I want to have a key for each page.
But when I use it on the view files, it reads just as a regular string:
<label for="email">
{{ __('login.email') }}
</label>
Any help would be nice, thank you very much.
Try:
<label for="email">
{{ __('login')['email'] }}
</label>
You can get this approach by changing the default __ helper by:
use Illuminate\Support\Arr;
function __($key = null, $replace = [], $locale = null)
{
// Default behavior
if (is_null($key)) return $key;
if (trans()->has($key)) return trans($key, $replace, $locale);
// Search in .json file
$search = Arr::get(trans()->get('*'), $key);
if ($search !== null) return $search;
// Return .json fallback
$fallback = Arr::get(trans()->get('*', [], config('app.fallback_locale')), $key);
if ($fallback !== null) return $fallback;
// Return key name if not found
else return $key;
}
Creating custom bootstrap helpers
if you dont know how to change the default helper, create a file with any name (ex: bootstrap/helpers.php), then in the file public/index.php add this line just before 'Register The Auto Loader'
/*
|--------------------------------------------------------------------------
| Register Custom Helpers
|------------------------------------------------------------------------
*/
require __DIR__.'/../bootstrap/helpers.php';
(Optional) Variables feature
if you also want to use the Variables feature, just like __(welcome.user, ['user'=>'david']) you must create a new helper on that file:
use Illuminate\Support\Str;
function trans_replacements($line, array $replace)
{
if (empty($replace)) return $line;
$shouldReplace = [];
foreach ($replace as $key => $value) {
$shouldReplace[':'.Str::ucfirst($key)] = Str::ucfirst($value);
$shouldReplace[':'.Str::upper($key)] = Str::upper($value);
$shouldReplace[':'.$key] = $value;
}
return strtr($line, $shouldReplace);
}
and then replace return $search with trans_replacements($search, $replace) and return $fallback with trans_replacements($fallback, $replace)
(Optional) Countable feature
for Countable feature (ex: 'an apple|many apples'), is the same process, just add this helper:
use Illuminate\Support\Facades\App;
function trans_choice($key, $number, array $replace = [], $locale = null)
{
// Get message
$message = __($key, $replace, $locale);
// If the given "number" is actually an array or countable we will simply count the
// number of elements in an instance.
if (is_array($number) || $number instanceof Countable)
$number = count($number);
$replace['count'] = $number;
return trans_replacements(
trans()->getSelector()->choose($message, $number, $locale = App::getLocale()),
$replace
);
}
Bootstrap Helpers File
here is the file, just in case: bendeckdavid/laravel_locale_nested_json
Laravel not supporting this by default is annoying.
However, you can use this function I made:
function ___($path) {
$properties = explode(".", $path);
$base = __($properties[0]);
unset($properties[0]);
foreach($properties as $property) {
// Allows specification of indexes
if(is_numeric($property)) {
$property = intval($property);
}
// If the key has not been found, return the initial parameter like __()
try {
$base = $base[$property];
} catch (\Throwable $th) {
return $path;
}
}
return $base;
}
If you are someone like me stumbling upon here wanting to use a file structure like below without the need to specify keys for each string when using php language files.
-app
-lang
--fr
---Headers.json
A little modification to David G's answer above,
<?php
use Illuminate\Support\Arr;
function ___($path = null, $replace = [], $locale = null)
{
// Default behavior
if (is_null($path)) return $path;
if(!strrpos($path, '.')) {
return $path;
}
$properties = explode(".", $path);
$currentLocale = app()->getLocale();
// If the file/folder doesn't exist
// return the last part of the dot notation
$translated_string = substr($path, strrpos($path, '.') + 1);
if(is_dir(lang_path($currentLocale))) {
if (is_file(lang_path($currentLocale.'\\'.$properties[0].'.json'))) {
$contents = json_decode(file_get_contents(lang_path($currentLocale.'\\'.$properties[0].'.json')), true);
// If no value exists for the key, the key is itself returned
$translated_string = Arr::get($contents, $properties[1], $properties[1]);
}
}
return $translated_string;
}
As of now this doesn't work for files under subdirectories under the locale folder. I am sure, the above code can be optimized, but this should give you an idea.
Also, this function uses triple-underscore {{___()}} instead of the regular double-underscore {{__()}} to avoid conflicts.
Works for,
{{___('Messages.Welcome') }} // lang/(locale)/Messages.json
{{___('Welcome')}} // lang/(locale).json
Assuming your json is stored in $json, you want to parse it into array by using json_decode
$data = json_decode($json, TRUE);
You can access it by:
<label for="email">
{{ $data['login']['emailPlaceholder'] }}
</label>
Hi tried refreshing the modification cache cache in opencart and since then i am getting a blank page in front end with this error message.
public function trigger($event, array $args = array()) {
foreach ($this->data as $value) {
if (preg_match('/^' . str_replace(array('\*', '\?'), array('.*', '.'), preg_quote($value['trigger'], '/')) . '/', $event)) {
$result = $value['action']->execute($this->registry, $args);
if (!is_null($result) && !($result instanceof Exception)) {
return $result;
}
}
}
}
Thanks,
It seems your have an OC version 3.0.2.x or above.
In your $this->data of the Event Class, you have an event registered that is missing an action parameter.
$this->data[] = array(
'trigger' => $trigger,
'action' => $action, // <-- this must be an Action Object with a method execute()
'priority' => $priority
);
All events are registered via the register() method which explicitly requests that an Action object is being passed as a parameter.
Since the error is pointing to "Call to undefined method Action::execute()", I can assume, you have an issue with the action class.
Most likely you need to check the Modifications of the system/engine/action.php file in your system/storage/modifications.
It could be that the method execute() is either missing or somehow corrupt.
Debug
try to var_dump the $value to see what is there:
public function trigger($event, array $args = array()) {
foreach ($this->data as $value) {
//log out the $value before the error to see if the Action object is actually there and see what trigger causes this.
var_dump($value);
if (preg_match('/^' . str_replace(array('\*', '\?'), array('.*', '.'), preg_quote($value['trigger'], '/')) . '/', $event)) {
$result = $value['action']->execute($this->registry, $args);
if (!is_null($result) && !($result instanceof Exception)) {
return $result;
}
}
}
}
Hope this helps
Hi tried refreshing the modification cache cache in opencart and since then i am getting a blank page in front end with this error message.
public function trigger($event, array $args = array()) {
foreach ($this->data as $value) {
if (preg_match('/^' . str_replace(array('\*', '\?'), array('.*', '.'), preg_quote($value['trigger'], '/')) . '/', $event)) {
$result = $value['action']->execute($this->registry, $args);
if (!is_null($result) && !($result instanceof Exception)) {
return $result;
}
}
}
}
Thanks,
It seems your have an OC version 3.0.2.x or above.
In your $this->data of the Event Class, you have an event registered that is missing an action parameter.
$this->data[] = array(
'trigger' => $trigger,
'action' => $action, // <-- this must be an Action Object with a method execute()
'priority' => $priority
);
All events are registered via the register() method which explicitly requests that an Action object is being passed as a parameter.
Since the error is pointing to "Call to undefined method Action::execute()", I can assume, you have an issue with the action class.
Most likely you need to check the Modifications of the system/engine/action.php file in your system/storage/modifications.
It could be that the method execute() is either missing or somehow corrupt.
Debug
try to var_dump the $value to see what is there:
public function trigger($event, array $args = array()) {
foreach ($this->data as $value) {
//log out the $value before the error to see if the Action object is actually there and see what trigger causes this.
var_dump($value);
if (preg_match('/^' . str_replace(array('\*', '\?'), array('.*', '.'), preg_quote($value['trigger'], '/')) . '/', $event)) {
$result = $value['action']->execute($this->registry, $args);
if (!is_null($result) && !($result instanceof Exception)) {
return $result;
}
}
}
}
Hope this helps
I'm trying to implement my custom Translation Filter in twig. All translations are stored in csv's. Per template we have a csv that contains the data.
Now for some reason the function within the filter isn't possible to access the $TranslationText array.
What should I change to make the filter working?
Notice: Undefined variable: TranslationText in /public_html/public/index.php on line 103
Translations are requested by {{ 'Whatever'|t}}
if (file_exists("../application/languages/nl/".$RenderTemplate.".csv") != FALSE) {
$handle = file_get_contents("../application/languages/nl/".$RenderTemplate.".csv");
$handle_row = explode("\n", $handle);
foreach ($handle_row as $key => $val) {
$row_array = explode('|', $val);
foreach ($row_array as $key => $val) {
$row_array[$key] = trim(str_replace('"', '', $val));
}
$TranslationText[] = $row_array;
}
}
require_once '../library/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem('../application/views/');
//Add Translation Filter
$filter = new Twig_SimpleFilter('t', function ($string) {
foreach($TranslationText AS $key => $value)//line 103
{
if($TranslationText[$key][0] == $string)
{
$Found = TRUE;
$TranslatedText = $TranslationText[$key][1];
}
}
if($Found == TRUE)
{
return $TranslatedText;
}
else
{
return $string;
}
});
$twig = new Twig_Environment($loader, array(
'cache' => '../tmp/cache/', 'auto_reload' => TRUE, 'debug' => TRUE
));
//REMOVE auto_reload and DEBUG if live!
$twig->addFilter($filter);
echo $twig->render($RenderTemplate.".html", $TotalArray);
All I can say is you get the error because of the following:
$filter = new Twig_SimpleFilter('t', function ($string) {
foreach($TranslationText AS $key => $value)//line 103
//... other code lines follow
});
You didn't use $translationText. Consider this code:
$filter = new Twig_SimpleFilter('t', function ($string) use ($TranslationText) {
foreach($TranslationText AS $key => $value)//line 103
//... other code lines follow
});
Please note that if your csv file doesn't exist or cannot be read, you will still get the same error, because you do not declare this variable outside your file_exists condition.
NB: I cannot guarantee that this is the only problem in your code. It's just a solution for your particular problem.
P.S. In general, your code looks messy. Consider using OOP for such things. Also, pay attention to the standards you use: some variables are named using underscores, others are named using CamelCase.
I'm trying to find all the files that called "testunit.php".
In addition i want to cut the first 23 chars of the string.
I tried this but this is not working.I get all the files.
$it = new RecursiveDirectoryIterator($parent);
$display = Array ( 'testunit.php');
foreach (new RecursiveIteratorIterator($it) as $file=>$cur) {
{
if ( In_Array ( $cur, $display ) == true )
$file = substr($cur, 23)
fwrite($fh,"<file>$file</file>");
}
Thank you!
see if glob helps you
Try
class TestUnitIterator extends FilterIterator
{
public function accept()
{
return (FALSE !== strpos(
$this->getInnerIterator()->current(),
'testunit.php'
));
}
public function current()
{
return sprintf(
'<file>%s</file>',
substr($this->getInnerIterator()->current(), 23)
);
}
}
Usage (codepad (abridged example)):
$iterator = new TestUnitIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
'/path/to/iterate/over',
FilesystemIterator::CURRENT_AS_PATHNAME
)
)
);
foreach ($iterator as $file) {
echo $file, PHP_EOL;
}
Disclaimer: I wasn't in the mood to mock the filesystem or setup the required test files, so the above might need a little tweaking to work with the filesystem. I only tested with an ArrayIterator but there shouldn't be much to do if the above produces errors.