Twig and error messages - is this normal? - twig

I've been testing Twig on localhost... the code here is the same as in this question but the query is different:
<?php
// include and register Twig auto-loader
include 'Twig/Autoloader.php';
Twig_Autoloader::register();
// attempt a connection
try {
$dbh = new PDO('mysql:dbname=world;host=localhost', 'root', 'mypass');
} catch (PDOException $e) {
echo "Error: Could not connect. " . $e->getMessage();
}
// set error mode
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// attempt some queries
try {
// execute SELECT query
// store each row as an object
$sql = "SELECT manufacturer, model, modelinfo FROM automobiles WHERE id = '4' ";
$sth = $dbh->query($sql);
while ($row = $sth->fetchObject()) {
$data[] = $row;
}
// close connection, clean up
unset($dbh);
// define template directory location
$loader = new Twig_Loader_Filesystem('templates');
// initialize Twig environment
$twig = new Twig_Environment($loader);
// load template
$template = $twig->loadTemplate('cars.html');
// set template variables
// render template
echo $template->render(array (
'data' => $data
));
} catch (Exception $e) {
die ('ERROR: ' . $e->getMessage());
}
?>
I have 3 records; I decided to query a non-existent record to see what Twig's error handling was like, as I was comparing Twig vs Smarty - out of interest, and for a project.
This error message comes up:
Notice: Undefined variable: data in /Applications/MAMP/htdocs/mysite/twigtesting.php on line 42
Surely a notice saying 'Data not found' should happen or am I wrong here?
Undefined variable data refers to:
// set template variables
// render template
echo $template->render(array (
'data' => $data
));
Why is this happening? I'm new to Twig, and using the latest build from their site, f that's relevant.

You don't get a Twig error, because the error does not exists in the templates, but in the code that is generating these templates.
PHP is having issues to put the value of $data inside an array, because that variable does not exists.
If you want to see how twig handles errors, you need to access a non existing variable inside a template. For instance, putting {{ notExisting }} in your current template.
I can already say that Twig is handling errors by throwing parsing exceptions in PHP. All exceptions thrown by Twig are extending Twig_Error. To catch these, use a try { ... } catch (\Twig_Error $e) { ... } block.
Furthermore, Twig can throw 3 different types of Exceptions:
Twig_Error_Syntax is thrown when an error occurs when parsing a template (e.g. using malformed tags).
Twig_Error_Loader is thrown when Twig can't load a file. This can happen when using a render() method, or when you use some file features in Twig (e.g. {% extends ... %}).
Twig_Error_RunTime is thrown when an error occurs in runtime (e.g. an error inside extensions).

Related

PHP __LINE__ equivalent for twig

There is a way to get PHP __LINE__ equivalent for Twig?
It's almost impossible to search __LINE__ on Google as exact word...
The purpose is purely for debugging a long twig file containing nested twig code inside a complex Js code, to console.log it.
The version of twig I'm using is 2.12.5.
Thanks
First things first:
As seen in the PHP documentation the (magic) constant will return the current line number of the file.The "problem" here is that twig converts templates to PHP in order to render them.
This means if you were actually were to be able to use __LINE__ inside a template, it would report back the line number from either a temporary PHP file or a cached PHP file, wether you have caching enabled or not.
TLDR: Using __LINE__ inside a template is going to report back "false"/useless information.
However, you can easily extend twig and even introduce new tags which you then can use inside templates. These customs tags allow you to modify/alter the compilation of the template's PHP file.
The interesting part here is that the "compiler" is able to provide your custom tag on which specific line the tag was called.
We can even create a custom tag, register it with twig and let the tag report back the line number in the parsed template.
Step 1 - Create a TokenParser
The TokenParser is responsible for parsing the template and allows you to choose a name for your tag. The code below will be responsible to create a simple, empty tag named line
<?php
namespace MyProject\Base\Twig\TokenParser;
use \MyProject\Base\Twig\Node\Line as LineNode;
class Line extends \Twig_TokenParser {
public function parse(\Twig_Token $token)
{
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new LineNode(new \Twig_Node(), $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'line';
}
}
Step 2 - Create a Node
The Node is responsible for converting the template code to actual PHP code
<?php
namespace MyProject\Base\Twig\Node;
class Line extends \Twig_Node {
public function __construct(\Twig_NodeInterface $body, $lineno, $tag = null) {
parent::__construct(['body' => $body,], array(), $lineno, $tag);
}
public function compile(\Twig_Compiler $compiler) {
$compiler->write('echo '.$this->getLine().';')
->write(PHP_EOL);
}
}
Step 3 - Register the tag with twig
<?php
namespace MyProject\Base\Twig;
class MyProjectTwigExtension extends Twig_Extension {
public function getTokenParsers() {
return [
new \CMS4U\Base\Twig\TokenParser\Line(),
];
}
public function getName() {
return 'MyProjectTwigExtension';
}
}
<?php
$twig->addExtension(new \MyProject\Base\Twig\MyProjectTwigExtension());
If everything is good, you can now use the custom tag {% line %} wherever you like in any template
Foo
Bar
FooBar
Current line number is {% line %} {# 4 #}

Opencart 3 extending Twig (access Twig environment from a controller)

In Opencart 3, is there a way to access twig environment from a controller?
working with OC v3.0.3.1
I am trying to add a custom function which I could use in the template.
I am using the documentation here
Tried adding the following to the controller:
$loader = new \Twig_Loader_Filesystem(DIR_TEMPLATE);
$config = array('autoescape' => false);
$twig = new \Twig_Environment($loader, $config);
$function= new \Twig_SimpleFunction('foo', function(){
return 'bar';
});
$twig->addFunction($function);
in the template I have:
{{ foo() }}
Getting:
Fatal error: Uncaught exception 'Twig_Error_Syntax' with message 'Unknown "foo" function in ...

manage hosts file on Windows using Puppet

I'm trying to manage my hosts file on a Windows machine using Puppet and Hiera. My problem is that I have never really used Hiera and I'm struggling with parsing the data content into a proper format.
The relevant section in hieradata/hiera.yaml looks like this:
myhosts : [
'host1 1.2.3.4',
'host2 2.3.4.5',
'host3 3.4.5.6']
I have code that uses a host module, but it also depends on a class that I don't have, so naturally it doesn't work.
class hosts::module (
$myhosts = hiera('myhosts'),
)
{
define update_hosts {
$value = split($name,' ')
host {
"${value[0]}" : ip => "${value[1]}",
}
}
update_hosts { $myhosts :; }
}
I have tried using the file resource instead of the host resource, and also tried doing it without any class, but for some reason I am getting this error
Error: Could not retrieve catalog from remote server: Error 500 on SERVER:
Server Error: Evaluation Error: Error while evaluating a Resource Statement,
Evaluation Error: Error while evaluating a Resource Statement, Duplicate
declaration: File[C:\Temp\tmp.txt] is already declared in file
/etc/puppetlabs/code/environments/production/manifests/site.pp:4; cannot redeclare
at /etc/puppetlabs/code/environments/production/manifests/site.pp:4
at /etc/puppetlabs/code/environments/production/manifests/site.pp:4:1
at /etc/puppetlabs/code/environments/production/manifests/site.pp:10 on node puppet-agent
As you can see, it claims that I have a duplicate declaration, but the weird thing is that it says it has a problem with the same line. It thinks it's declaring the same thing twice for some reason.
This is the code I have now (I know it won't work but the error doesn't really sound related)
define hosts_update($content) {
file { 'C:\Temp\tmp.txt' :
ensure => file,
content => $content,
}
}
hosts_update{ hiera('myhosts'):
content => split($name," "),
}
Any idea how to do this right?
fixed it.
site.pp
include update_hosts
init.pp
class update_hosts::host
(
$hosts = hiera('hosts_list'),
)
{
update_host { $hosts :; }
}
host.pp
define update_host {
​
$value = split($name,' ')
​
host {
"${value[0]}" : ip => "${value[1]}",
target => "C:/Windows/System32/drivers/etc/hosts"
}
}

Twig_Template::getSource() doesn't work until the template has been cached

I need to load and show a Twig template's source.
The template's getSource() method appears to work by using reflection to find its own class file, and reading the comment block at the end of it (which has the Twig code).
public function getSource()
{
$reflector = new ReflectionClass($this);
$file = $reflector->getFileName();
// ...
}
Unfortunately, that file is only available when the template is loaded from the file cache - before then, the class is defined at runtime and the ReflectionClass will return Environment.php(403) : eval()'d code as the class file.
if (!class_exists($cls, false)) {
$content = $this->compileSource($this->getLoader()->getSource($name), $name);
if ($this->bcWriteCacheFile) {
$this->writeCacheFile($key, $content);
} else {
$this->cache->write($key, $content);
}
eval('?>'.$content);
}
Is there any other way I can get the source from Twig, or is it only possible if I find and read the original .html.twig file directly?
Oops. The answer was right there in the code, of course:
$content = $this->compileSource($this->getLoader()->getSource($name), $name);
All I needed was to replace $environment->loadTemplate($name)->getSource() with $environment->getLoader()->getSource($name).

Express, check if a template exists

Is there a way I can tell if a given template exists in express? Basically I want to create specific and fallback templates but don't want to contain that logic in the template itself.
if( res.templateExists( 'specific_page' ) ) {
res.render( 'specific_page' );
} else {
res.render( 'generic_page' );
}
The specific_page name is generatead at runtime based on the users device, language, etc.
NOTE: I don't need to know how to do string localization within a template, that I already have. I'm looking for cases where the entire layout/template changes.
You could use this:
res.render('specific_page', function(err, html) {
if (err) {
if (err.message.indexOf('Failed to lookup view') !== -1) {
return res.render('generic_page');
}
throw err;
}
res.send(html);
});
This will distinguish between an error thrown because the template couldn't be found (in which case it will render generic_page instead), and any other errors that might occur (which are re-thrown). It's not entirely stable because it relies on the error message that's being thrown, but I don't think there's any other way of determining the type of error.

Resources