Im using phalcon 2.0.0 and i am trying to call a function with in another function but from the same class like shown below, for some reason i get a blank page. And when i comment the calling of 2nd function from first, the page loads properly.
<?php
use Phalcon\Mvc\User\Component;
class Testhelper extends Component {
public function f1($data) {
$tmp = $this->f2($data);
return $tmp;
}
public function f2($data) {
return '5'; // just testing
}
}
And btw im accessing the f1 function by the volt function extender like this
$compiler->addFunction('customfunc', function($resolvedArgs, $exprArgs) {
return 'Testhelper ::f1('.$resolvedArgs.')';
});
if someone could help me, it would be deeply appreciated.
Thanks guys
You are trying to call TestHelper f1() statically in Volt, where your class does not expose that function as a static.
You can change your code like this:
<?php
use Phalcon\Mvc\User\Component;
class Testhelper extends Component
{
public static function f1($data)
{
$tmp = self::f2($data);
return $tmp;
}
public static function f2($data)
{
return '5'; // just testing
}
}
and your Volt function will work. However you have to bare in mind that because you are calling things statically you won't have immediate access to all the di container services that the Component offers like so:
$this->session
$this->db
You will need to modify your code to pick the di container using the getDefault()
Another option is to use the code as you have right now, but register the TestHelper in your di container like so:
$di->set(
'test_helper',
function () {
return new TestHelper();
}
);
and then your volt function will need to change to:
$compiler->addFunction(
'customfunc',
function ($resolvedArgs, $exprArgs) {
return '$this->test_helper->f1('.$resolvedArgs.')';
}
);
Related
I'm attempting to build a role based access control in our PHP framework. The framework is on MVC architecture so every path works on /controller/action/param. We can get the controller and action on initialization and store them in variables, $controller, $action. Now my idea is to use a class to check the permissions of this action like:
Auth::permissions($controller, $action);
Now I'm hoping I could somehow create a script which would find all public methods of controllers inside a /modules/ folder. This way I could just run a script and it would update all controller actions as a list to the database, where we would get the role permissions from. This way I could avoid inserting all controller actions manually. Getting all the controllers is very easy as the folder structure is as:
/modules
/controller
controller.php
So I can just find all subdirectories on modules and add .php in the end. My question is that can I get the file's public methods somehow?
class Example extends Controller {
public function main() {
return 'foo';
}
}
This way I could store this in the database as
example | main | role_id
Here is a little code that can help you:
<?php
class Example {
public function main() {
return 'foo';
}
private function privatefunc(){
}
public function anotherpublicfunc(){
}
}
$reflector = new ReflectionClass("Example");
foreach($reflector->getMethods() as $method){
if($method->isPublic()) {
echo "Method ".$method->name." is public".PHP_EOL;
}else{
echo "Method ".$method->name." is not public".PHP_EOL;
}
}
?>
output:
Method main is public
Method privatefunc is not public
Method anotherpublicfunc is public
If you want to get public methods of a class then you can use get_class_methods read the doc here
class Car {
public function permission_method_two() {
}
public function permission_method_three() {
}
private function private_function() {
}
}
echo '<pre>'.print_r(get_class_methods('Car'),1).'</pre>';
// prints only public methods:
Array
(
[0] => permission_method_two
[1] => permission_method_three
)
You can follow convention:
- each public methods start without lowdash
- each private and protected method start with lowdash
Example
class Example
{
public function publicMethod()
{
}
private function _privateMethod()
{
}
protected function _protectedMethod()
{
}
}
and then use http://php.net/manual/ru/function.get-class-methods.php
foreach(get_class_methods('Example') as $methodName){
if(strpos($methodName, '_') !== 0) $publicMethod[] = $methodName;
}
I am trying to initialize data in index function of controller, so that initialized data can be used in subsequent functions of controller. But the problem is data is not being displayed when I am trying to access it from other function. All of this is just to follow a sort of object oriented pattern.
Here is my code.
class Dashboard extends CI_Controller
{
private $account_data; /*Declaration*/
private $profile_data;
function __construct() {
// code...
}
function index() /*Here I am initializing data*/
{
$this->load->model('db_model');
$this->account_data = $this->db_model->get_row();
$this->profile_data = $this->db_model->get_row();
$this->load->view('user/dashboard');
}
function function account_details()
{
print_r($this->account_data); // This displays nothing
}
/*other function...*/
}
Idea is to get data once and use it for other functions and if data is updated again calls a function to initialize it.
But it is not working out. Please help me. Also suggest if I am following right approach.
Thanks for your time.
index method is not initializer, its default page/sub_method,
if you call the "*account_details*" in url as index.php/dashboard/account_details the index wont be called.
try put the code on constructor,
class Dashboard extends CI_Controller
{
private $account_data; /*Declaration*/
private $profile_data;
function __construct() { /*Here I am initializing data*/
parent::CI_Controller(); // Thank you Sven
$this->load->model('db_model');
$this->account_data = $this->db_model->get_row();
$this->profile_data = $this->db_model->get_row();
}
function index()
{
$this->load->view('user/dashboard');
}
function function account_details()
{
print_r($this->account_data); // This displays nothing
}
/*other function...*/
}
Note : don't the models or other computations on __construct() if you don't need on all methods of this controller.
create a private method like "model_initializer()" put this codes on this scope, and the call it in your other methos as $this->model_initialize(); if you need.
Thanks yo Sesama Sesame for note,
I am working on a script, and I need to make it pluginable. Now the syntax I have come with and which should work for me, is to make it use classes. For example, in order to create a new plugin that would be run when a certain point (hook) is reached, you would declare a new class. What I am not sure is how would the hook be specified in that syntax, so I am looking for suggestions.
Syntax example:
<?php
class ScriptPlugin
{
function runPlugin() {} // would be run when the time has to come to execute this plugin
}
?>
Also, if that syntax is not going to work, it would be great if you guys could give me a good syntax example.
There is the Observer Pattern which comes to mind. Plugins will register themselves and will get notifications when the hook is invoked.
Another thing that comes to mind are callbacks in PHP. And there was a similar question already with an answer that came to mind. It shows hooks based on callbacks.
The Observer Pattern runs a bit short because with hooks you often want to provide things like arguments and a return value. The linked answer which uses callbacks does not have this either, so I wrote a little Hooks example class that provides named hooks/events to registered callbacks, and a way to register your own classes, e.g. a plugin.
The idea is pretty basic:
A hook has a name and zero or more callbacks attached.
All hooks are managed in a Hooks class.
The main code invokes hooks by calling a function on the Hooks object.
Plugins (and other classes) can register their own callbacks, which is done with the help of the Registerable interface.
Some example code with one plugin and two hooks:
<?php
Namespace Addon;
class Hooks
{
private $hooks = array();
private $arguments;
private $name;
private $return;
public function __call($name, array $arguments)
{
$name = (string) $name;
$this->name = $name;
$this->arguments = $arguments;
$this->return = NULL;
foreach($this->getHooks($name) as $hook)
{
$this->return = call_user_func($hook, $this);
}
return $this->return;
}
public function getHooks($name)
{
return isset($this->hooks[$name]) ? $this->hooks[$name] : array();
}
public function getArguments()
{
return $this->arguments;
}
public function getName()
{
return $this->name;
}
public function getReturn()
{
return $this->return;
}
public function setReturn($return)
{
$this->return = $return;
}
public function attach($name, $callback)
{
$this->hooks[(string) $name][] = $callback;
}
public function register(Registerable $plugin)
{
$plugin->register($this);
}
}
interface Registerable
{
public function register(Hooks $hooks);
}
class MyPlugin implements Registerable
{
public function register(Hooks $hooks)
{
$hooks->attach('postPublished', array($this, 'postPublished'));
$hooks->attach('postDisplayFilter', array($this, 'filterToUpper'));
}
public function postPublished()
{
echo "MyPlugin: postPublished.\n";
}
public function filterToUpper(Hooks $context)
{
list($post) = $context->getArguments();
return strtoupper($post);
}
}
$hooks = new Hooks();
$plugin = new MyPlugin();
$hooks->register($plugin);
$hooks->postPublished();
echo $hooks->postDisplayFilter("Some post text\n");
I've done it this way to prevent that each Plugin must have a concrete base class only because it wants to make use of hooks. Additionally everything can register hooks, the only thing needed is a callback. For example an anonymous function:
$hooks->attach('hookName', function() {echo "Hook was called\n";});
You can however create yourself a plugin base class, that for example implements the register function and will automatically register functions that have a certain docblock tag or the name of a function
class MyNewPlugin extends PluginSuper
{
/**
* #hook postPublished
*/
public function justAnotherFunction() {}
public hookPostPublished() {}
}
The superclass can make use of Reflection to add the hooks on runtime. However reflection can slow things down and might make things harder to debug.
Let's say a plugin is like :
class NewsPlugin extends Plugin
{
function onCreate($title)
{
# Do some stuff
}
}
Then when you create a news you can just call onCreate on all plugins registered.
I would make a base abstract class with functions for all the hooks that could possibly be called.
abstract class Plugin {
abstract function yourHook();
}
All plugin classes should inherit this base class, and will override those base functions with their own.
class SomePlugin extends Plugin {
function yourHook() {
echo 'yourHook() Called!';
}
}
Now when your program runs, you need to find all of those plugin files to include, and somehow put them into an array, such as $plugins. See this article: https://stackoverflow.com/a/599694/362536
foreach (glob("classes/*.php") as $filename)
{
include $filename;
}
(From Karsten)
Define a function accessible from everything, such as registerPlugin():
function registerPlugin($classname) {
$plugins[] = new $classname();
}
Make the top line of each plugin file like this (prior to the class):
registerPlugin('SomePlugin');
If you do this, you'll have an array in $plugins with instances of each plugin. At the appropriate time, you can do something like this:
foreach ($plugins as $plugin) {
$plugin->yourHook();
}
As an alternative, it may be more appropriate to use interfaces in your case, instead. You should decide which method is best for your application.
I am working on creating my own very simple MVC and I am brainstorming ways to go from the controller to the view. Which involves sending variables from a class to just a plain old PHP page.
I am sure that this has been covered before, but I wanted to see what kind of ideas people could come up with.
//this file would be /controller/my_controller.php
class My_Controller{
function someFunction(){
$var = 'Hello World';
//how do we get var to our view file in the document root?
//cool_view.php
}
}
Some kind of hashtable is a good way to do that. Return your variables as association array which will fill all the gaps in your view.
Store your variables as a property in your controller object, then extract them when rendering
class My_Controller {
protected $locals = array();
function index() {
$this->locals['var'] = 'Hello World';
}
protected function render() {
ob_start();
extract($this->locals);
include 'YOUR_VIEW_FILE.php';
return ob_get_clean();
}
}
You can define those magic __get and __set methods to make it prettier
$this->var = 'test';
I'm also developing my own simple MVC and the most simple way to do it is ...
class My_Controller
{
function someFunction() {
$view_vars['name'] = 'John';
$view = new View('template_filename.php', $view_vars);
}
}
View class
class View
{
public function __construct($template, $vars) {
include($template);
}
}
template_filename.php
Hello, <?php echo $vars['name'];?>
I highly recommend you to take a look at PHP Savant http://phpsavant.com/docs/
I'd checkout Zend_View and how it accomplished view rendering.
You can get the source of View and AbstractView on github - unfortunaly I don't find the current repository (in svn) that easy to browse.
Essentially the view variables are contained in a View object (which your controller would have access to), then the template (plain old php document) is rendered inside that object. That method allows the template access to $this.
It would be something like:
<?php
class View
{
public function render()
{
ob_start();
include($this->_viewTemplate); //the included file can now access $this
return ob_get_clean();
}
}
?>
So in your controller:
<?php
class Controller
{
public function someAction()
{
$this->view->something = 'somevalue';
}
}
?>
And your template:
<p><?php echo $this->something;?></p>
In my opinion this pattern allows you much flexibility with the view.
I created my own MVC for the free PHP course I'm conducting for a handful of people wanting to get better at PHP.
By far the best way to do this is to use the Command + Factory pattern.
E.g.
interface ControllerCommand
{
public function execute($action);
}
In each controller:
class UserController implements ControllerCommand
{
public function execute($action)
{
if ($action == 'login')
{
$data['view_file'] = 'views/home.tpl.php';
}
else if ($action == 'edit_profile')
{
$data['view_file'] = 'views/profile.tpl.php';
$data['registration_status'] = $this->editProfile();
}
return $data;
}
}
From your main front controller:
$data = ControllerCommandFactory::execute($action);
if (!is_null($data)) { extract($data); }
/* We know the view_file is safe, since we explicitly set it above. */
require $view_file;
The point is that every Controllercommand class has an execute function and that returns its view and any data for that view.
For the complete MVC, you can access the open source app by emailing me at theodore[at]phpexperts.pro.
I have a PHP web application built with CodeIgniter MVC framework. I wish to test various controller classes. I'm using Toast for unit testing. My controllers have no state, everything they process is either saved into session or passed to view to display. Creating a mock session object and testing whether that works properly is straightforward (just create a mock object and inject it with $controller->session = $mock).
What I don't know, is how to work with views. In CodeIgniter, views are loaded as:
$this->load->view($view_name, $vars, $return);
Since I don't want to alter CI code, I though I could create a mock Loader and replace the original. And here lies the problem, I cannot find a way to derive a new class from CI_Loader.
If I don't include the system/libraries/Loader.php file, the class CI_Loader is undefined and I cannot inherit from it:
class Loader_mock extends CI_Loader
If I do include the file (using require_once), I get the error:
Cannot redeclare class CI_Loader
Looks like CI code itself does not use require_once from whatever reason.
Does anyone here have experience with unit testing CodeIgniter powered applications?
Edit: I tried to inject a real loader object at run-time into a mock class, and redirect all calls and variables with __call, __set, __get, __isset and __unset. But, it does not seem to work (I don't get any errors though, just no output, i.e. blank page from Toast). Here's the code:
class Loader_mock
{
public $real_loader;
public $varijable = array();
public function Loader_mock($real)
{
$this->real_loader = $real;
}
public function __call($name, $arguments)
{
return $this->real_loader->$name($arguments);
}
public function __set($name, $value)
{
return $this->real_loader->$name = $value;
}
public function __isset($name)
{
return isset($this->real_loader->$name);
}
public function __unset($name)
{
unset($this->loader->$name);
}
public function __get($name)
{
return $this->real_loader->$name;
}
public function view($view, $vars = array(), $return = FALSE)
{
$varijable = $vars;
}
}
Alternatively, you could do this:
$CI =& get_instance();
$CI = load_class('Loader');
class MockLoader extends CI_Loader
{
function __construct()
{
parent::__construct();
}
}
Then in your controller do $this->load = new MockLoader().
My current solution is to alter the CodeIgniter code to use require_once instead of require. Here's the patch I'm going to send to CI developers in case someone needs to do the same until they accept it:
diff --git a/system/codeigniter/Common.php b/system/codeigniter/Common.php
--- a/system/codeigniter/Common.php
+++ b/system/codeigniter/Common.php
## -100,20 +100,20 ## function &load_class($class, $instantiate = TRUE)
// folder we'll load the native class from the system/libraries folder.
if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT))
{
- require(BASEPATH.'libraries/'.$class.EXT);
- require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
+ require_once(BASEPATH.'libraries/'.$class.EXT);
+ require_once(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
$is_subclass = TRUE;
}
else
{
if (file_exists(APPPATH.'libraries/'.$class.EXT))
{
- require(APPPATH.'libraries/'.$class.EXT);
+ require_once(APPPATH.'libraries/'.$class.EXT);
$is_subclass = FALSE;
}
else
{
- require(BASEPATH.'libraries/'.$class.EXT);
+ require_once(BASEPATH.'libraries/'.$class.EXT);
$is_subclass = FALSE;
}
}
I can't help you much with the testing, but I can help you extend the CI library.
You can create your own MY_Loader class inside /application/libraries/MY_Loader.php.
<?php
class MY_Loader extends CI_Loader {
function view($view, $vars = array(), $return = FALSE) {
echo 'My custom code goes here';
}
}
CodeIgniter will see this automatically. Just put in the functions you want to replace in the original library. Everything else will use the original.
For more info check out the CI manual page for creating core system classes.
I'm impressed by the code you are trying to use.
So now I'm wondering how the 'Hooks' class of CodeIgniter could be of any help to your problem?
http://codeigniter.com/user_guide/general/hooks.html
Kind regards,
Rein Groot
The controller should not contain domain logic, so unit tests make no sense here.
Instead I would test the controllers and views with acceptance tests.