PHP Twig extension error - php

Im trying to create a twig extension
$loader = new \Twig_Loader_Filesystem(__DIR__.'/../views');
$this->layout = new \Twig_Environment($loader, array(
'cache' => '/../views/cache',
'auto_reload' => true
));
$this->layout->addExtension(new \App\Lib\twig_microtime());
And App\Lib\twig_microtime
class Twig_microtime extends \Twig_Extension {
private $start;
public function getFunctions() {
return array(
'microtime_start' => new \Twig_SimpleFilter($this, 'microtimeStart'),
'microtime_end' => new \Twig_SimpleFilter($this, 'microtimeEnd')
);
}
public function microtimeStart() {
$this->start = microtime(true);
}
public function microtimeEnd() {
return 'eeeee';
}
public function getName() {
return 'microtime_extension';
}
}
So at my layout Im trying to call {{ microtime_end() }} but im getting this error
An exception has been thrown during the compilation of a template ("Argument 2 passed to Twig_NodeVisitor_SafeAnalysis::setSafe() must be of the type array, null given

First you define Filters in the getFunctions method, if these are Filters define them in the getFilters method.
Then the Twig_SimpleFilter and Twig_SimpleFunction object expects an array as the 2nd argument.
So try this:
public function getFilters() {
return array(
new \Twig_SimpleFilter('microtime_start', array($this, 'microtimeStart')),
new \Twig_SimpleFilter('microtime_end', array($this, 'microtimeEnd'))
);
}
But i guess you actually mean to create Functions.
This would be so:
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('microtime_start', array($this, 'microtimeStart')),
new \Twig_SimpleFunction('microtime_end', array($this, 'microtimeEnd'))
);
}

Related

How to use (chain?) multiple normalizers with Symfony Serializer?

can somebody try to explain me how to use multiple normalizers when serializing data from multiple classes with the Symfony serializer?
Lets say that I have the following classes:
class User
{
private $name;
private $books;
public function __construct()
{
$this->books = new ArrayCollection();
}
// getters and setters
}
class Book
{
private $title;
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
}
}
And I want to serialize an user who has multiple books.
$first = new Book();
$first->setTitle('First book');
$second = new Book();
$second->setTitle('Second book');
$user = new User();
$user->setName('Person name');
$user->addBook($first);
$user->addBook($second);
dump($this->get('serializer')->serialize($user, 'json'));
die();
Let's say that I also want to include a hash when serializing a book, so I have the following normalizer:
class BookNormalizer implements NormalizerInterface
{
public function normalize($object, $format = null, array $context = array())
{
return [
'title' => $object->getTitle(),
'hash' => md5($object->getTitle())
];
}
public function supportsNormalization($data, $format = null)
{
return $data instanceof Book;
}
}
And I am getting the expected result:
{"name":"Person name","books":[{"title":"First book","hash":"a9c04245e768bc5bedd57ebd62a6309e"},{"title":"Second book","hash":"c431a001cb16a82a937579a50ea12e51"}]}
The problem comes when I also add a normalizer for the User class:
class UserNormalizer implements NormalizerInterface
{
public function normalize($object, $format = null, array $context = array())
{
return [
'name' => $object->getName(),
'books' => $object->getBooks()
];
}
public function supportsNormalization($data, $format = null)
{
return $data instanceof User;
}
}
Now, the books aren't normalized using the previously given normalizer, and i get the following:
{"name":"Person name","books":[{},{}]}
I tried to find a way (documentation and other articles) to always call the normalizers for the given types (eg. always call the book normalizer when the type is Book, even if the data is nested and used in another normalizer) but could not succeed.
I think i have misunderstood something about normalizers but don't know what. Can somebody explain to is what i want possible and how to do it?
You have to use the NormalizerAwareTrait so you can access the normalizer for books
add interface
use trait
call normalize() method for books
code:
class UserNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
public function normalize($object, $format = null, array $context = array())
{
return [
'name' => $object->getName(),
'books' => $this->normalizer->normalize($object->getBooks(), $format, $context)
];
}
public function supportsNormalization($data, $format = null)
{
return $data instanceof User;
}
}

Dependency Injection - Cannot use object of type Closure as array

I am trying to integrate a simple dependency injection design pattern in my application since it is only small. Here is the class:
class Factory
{
private static $_models = array();
public static function set(
$callback,
$controller,
$args = array()
) {
if( is_callable( $controller ) ) {
// store the controller and args to the array key callback
self::$_models[$callback] = array(
'args' => $args,
'controller' => $controller
);
return;
}
// if the controller isn't a function return
throw new Exception("Undefined Closure.");
}
public static function get(
$callback,
$action
) {
if(self::$_models[$callback]) {
// execute the closure to receive controller array
$prefix = call_user_func(self::$_models[$callback]['controller']);
if(is_callable($prefix[$action])) {
// execute the closure with or without args
return count($prefix['args']) < 0 ? $prefix[$action]($prefix['args']) : $prefix[$action]();
}
// couldn't find the function so return an error
throw new Exception("Undefined action: $action.");
}
// couldn't find the callback so return an error
throw new Exception("Undefined callback: $callback.");
}
}
When I try to set a controller like this:
Factory::set('example', function($arg) {
return array(
'controller' => function() {
global $arg;
return $arg;
}
);
}, array('value'));
I receive this error:
Uncaught Error: Cannot use object of type Closure as array
And the code doesn't get to this part:
print_r(Factory::get('example', 'controller')); // expected output array: 'value'

__callStatic not being called

Not sure why but Its not even hitting the var_dump() that I have. Lets look at how I have it implemented.
<?php
namespace ImageUploader\Controllers;
class ApplicationController implements \Lib\Controller\BaseController {
....
public function beforeAction($actionName = null, $actionArgs = null){}
public function afterAction($actionName = null, $actionArgs = null){}
public static function __callStatic($name, $args) {
var_dump('hello?'); exit;
if (method_exists($this, $name)) {
$this->beforeAction($name, $args);
$action = call_user_func(array($this, $name), $args);
$this->afterAction($name, $args);
return $action;
}
}
}
As we can see I want to do something before and after an action is called, regardless if you implemented the method or not. But that var_dump is never reached.
This class is extended in:
<?php
namespace ImageUploader\Controllers;
use \Freya\Factory\Pattern;
class DashboardController extends ApplicationController {
public function beforeAction($actionName = null, $actionArgs = null) {
var_dump($actionName, $actionArgs); exit;
}
public static function indexAction($params = null) {
Pattern::create('\Freya\Templates\Builder')->renderView(
'dash/home',
array(
'flash' => new \Freya\Flash\Flash(),
'template' => Pattern::create('\Freya\Templates\Builder')
)
);
}
....
}
Now when I do: DashboardController::indexAction(); it should exit ... unless I am missing something. If that's the case - what is it?
even the var_dump in the before_action(...) that's implemented is never reached (obvi' because of the first one, but if I take out the first one the second is never reach.)
__callStatic is called only when a static method does not exist - as indexAction actually is defined, it is executed without bothering __callStatic(). (Documentation)
An approach to achieve what you are trying to do could be by wrapping your controller inside a decorator:
class ExtendedApplicationController
{
/**
* #var \Lib\Controller\BaseController
*/
protected $controller;
function __construct(\Lib\Controller\BaseController $controller) {
$this->controller = $controller;
}
function __callStatic($name, $args) {
if (method_exists($this->controller, 'beforeAction')) {
call_user_func_array(array($this->controller, 'beforeAction'), $name, $args);
}
call_user_func_array(array($this->controller, $name), $args);
if (method_exists($this->controller, 'afterAction')) {
call_user_func_array(array($this->controller, 'afterAction'), $name, $args);
}
}
}
and then, in your code, you could do:
$controller = new ExtendedApplicationController(new DashboardController());
$controller::indexAction();
I have to warn you that I didn't test this approach while I was writing it, but I hope it gives you an idea!

Use $this in callback refer to callback class

So, I have something like this:
class ClassA{
public function doCallback($callback){
call_user_func_array($callback, array());
}
public function doSomething(){
return 12345;
}
}
class ClassB{
public function runMe(){
$classA = new ClassA();
$classA->doCallback(function(){
$this->doSomething();
});
}
}
I am trying to figure out how, if possible I can use $this or something similar in a callback function that will refer to the class that the callback is running on (Not the class it is in) if that makes sense.
So in my above example I would like $this->doSomething(); where $this means ClassA and not ClassB. Currently $this is referring to ClassB. Is there something that I can use to say I want ClassA?
EDIT
Here is the actual method that I am using
public function save(){
$id = (int)$this->input->post("id");
$title = $this->input->post("title");
$uid = (int)$this->session->get("id");
$this->db->getTable("content")
->has(array("user_id" => $uid, "name" => $title), function(){
echo json_encode(array("error" => "Name already exists."));
}, function() use($id, $uid, $title){
//$this->db->getTable("content")
$this->update(array("name" => $title), array(
"user_id" => $uid,
"content_id" => $id), function($rows){
if($rows > 0){
$error = "";
}else{
$error = "Unknown error occurred";
}
echo json_encode(array("error" => $error));
});
});
}
$this->db->getTable("content") returns a Database Object, and has() is a method in the object. I was hoping that I could use a shorthand way to access $this->db->getTable("content") without having to call it again in the callback, or passing it through call_user_func_array as a parameter or without the use of use().
the method has():
https://github.com/ZingPHP/Zing/blob/master/Zing/src/Modules/Database/DBOTable.php#L311
EDIT
I think in my callback function I need to do something like this, but I don't think it is working:
public function myFunc(callback $callback){
$callback->bindTo($this, $this);
return call_user_func_array($callback, array());
}
You can go through the public interface of ClassA.
class ClassA
{
public function doCallback($callback)
{
call_user_func_array($callback, array());
}
public function doSomething()
{
echo "Doing something...\n";
}
}
class ClassB
{
private $_oSubject;
public function __construct(ClassA $oSubject)
{
$this->_oSubject = $oSubject;
}
public function runMe()
{
$this->_oSubject->doCallback(function() {
$this->_oSubject->doSomething();
});
}
}
$oA = new ClassA();
$oB = new ClassB($oA);
$oB->runMe();
I got it! I just need to add $newCallback = $callback->bindTo($this, $this); in the callback class, and then in ClassB I can use $this to refer to ClassA.
class ClassA{
public function doCallback($callback){
$callback = $callback->bindTo($this, $this);
call_user_func_array($callback, array());
}
public function doSomething(){
return 12345;
}
}
class ClassB{
public function runMe(){
$classA = new ClassA();
$classA->doCallback(function(){
$this->doSomething();
});
}
}

ZF2 Is there a shorter/cleaner way to get the TableGateway classes in a controller?

Recently I made my first ZF2 application. I was walking through the code to see if I could make the code somewhat cleaner. Then I noticed that my controller classes have a huge block of code that supplies the controller of the TableGateway classes it needs. And I wondered is there a shorter/cleaner way to do this? It just seems silly that half of my controller class is dedicated to this simple task of fetching some TableGateWay classes.
protected $appointmentTable;
protected $customerTable;
protected $serviceTable;
protected $locationTable;
// ... some action methods that actually do the work.
public function getAppointmentTable()
{
if (!$this->appointmentTable) {
$sm = $this->getServiceLocator();
$this->appointmentTable = $sm->get('Appointment\Model\AppointmentTable');
}
return $this->appointmentTable;
}
public function getServiceTable()
{
if (!$this->serviceTable) {
$sm = $this->getServiceLocator();
$this->serviceTable = $sm->get('Appointment\Model\ServiceTable');
}
return $this->serviceTable;
}
public function getLocationTable()
{
if (!$this->locationTable) {
$sm = $this->getServiceLocator();
$this->locationTable = $sm->get('Appointment\Model\LocationTable');
}
return $this->locationTable;
}
public function getCustomerTable()
{
if (!$this->customerTable) {
$sm = $this->getServiceLocator();
$this->customerTable = $sm->get('Customer\Model\CustomerTable');
}
return $this->customerTable;
}
The way your Controllers should ideally be set up is through the means of proper(!) dependency injection. In Zend Framework 2 you have two main ways to declare controllers within the ControllerManager. The first one being invokables for controllers who have no dependencies and the second one being factories for controllers who have dependencies.
Any TableGateway always is a dependency. To my experience there are no controllers who are invokables at all :P
There's two ways to set up controller factories.
Module.php using getControllerConfig()
Under the controllers[factories] key in your module.config.php using Factory-Classes
For simplicity I'll choose the first approach now:
public function getControllerConfig()
{
return array(
'factories' => array(
'My\Foo\Controller' => function ($cpm) {
//#var $cpm \Zend\Mvc\Controller\ControllerManager
$serviceLocator = $cpm->getServiceLocator();
$tableGateway = $serviceLocator->get('My\Table\Gateway');
return new \My\Foo\Controller($tableGateway);
}
)
);
}
With this, all that's left is for you to modify your controller and have it pass the respective tablegateway inside its constructor:
class Controller
{
protected $tableGateway;
public function __construct(\My\Table\Gateway $tg)
{
$this->tableGateway = $tg;
}
public function indexAction()
{
return new ViewModel(array(
'entries' => $this->tableGateway->select()
));
}
}
And that's all there is to it. It's all about proper dependency injection that makes your life ultimately so much easier.
Obviously this example only covers one table, but you can do the same just passing more tables through the constructor. That is: only if you really need ALL TableGateways in there (which sounds a bit fishy) ;)
Could you just simplify the process in another method? I'm not aware of this function in Zend2, but still, if there is no method on framework level, you can write your own simplified method
My test so far:
public function setTable($method) {
$method = lcfirst(str_replace("get", "", $method));
$this->$method = 'Apointment\Model\\'.ucfirst($method);
return $this->$method;
}
public function getLocationTable() {
$this->setTable(__FUNCTION__);
var_dump(get_object_vars($this));
}
Outputs:
array (size=1)
'locationTable' => string 'Apointment\Model\LocationTable' (length=30)
So you can change setTable() method to use your set() proxy:
public function setTable($method) {
$method = lcfirst(str_replace("get", "", $method));
if (!$this->$method) {
$sm = $this->getServiceLocator();
$this->$method = $sm->get('Apointment\Model\\'.ucfirst($method));
}
return $this->$method;
}
public function getLocationTable() {
return $this->setTable(__FUNCTION__);
}
public function getServiceTable() {
return $this->setTable(__FUNCTION__);
}
Or you can get all your tables in array, iterate through it and pass the name to your setTable() method, which will set inner properties.
My string test (because I don't have ZF2 right here, and testing if the proper string which you are passing to the set() proxy is built:
class Tables {
public function setTable($method) {
$method = lcfirst(str_replace("get", "", $method));
$this->$method = 'Apointment\Model\\'.ucfirst($method);
/*if (!$this->$method) {
$sm = $this->getServiceLocator();
$this->$method = $sm->get('Apointment\Model\\'.ucfirst($method));
}*/
return $this->$method;
}
public function getLocationTable() {
return $this->locationTable;
}
public function getServiceTable() {
return $this->serviceTable;
}
public function getAppointmentTable() {
return $this->appointmentTable;
}
public function setAllTables() {
foreach (get_class_methods(__CLASS__) as $method) {
if (strpos($method, 'get')!== false && strpos($method, 'Table')!==false)
$this->setTable($method);
}
}
}
$tables = new Tables();
$tables->setAllTables();
var_dump(get_object_vars(($tables)));
Outputs:
array (size=3)
'locationTable' => string 'Apointment\Model\LocationTable' (length=30)
'serviceTable' => string 'Apointment\Model\ServiceTable' (length=29)
'appointmentTable' => string 'Apointment\Model\AppointmentTable' (length=33)
Now all your get____Table() methods are valid getters. E.g.:
var_dump($tables->getServiceTable());
returns
string 'Apointment\Model\ServiceTable' (length=29)

Categories