Phalcon: global keyword doesn't work inside Index Controller - php

This post may be long and messy but oh well... So i have my website in raw PHP
My original file structure:
/Index.php
/Users.php
/Smarty.class.php
/db.php
/Stats.php
/css/
/js/
Now i want to "port" each index file into phalcon controllers, like this:
/Controllers/IndexController.php
/Controllers/UsersController.php
/Smarty.class.php
/db.php
/css/
/js/
The thing is that global keyword doesnt work inside IndexController.php:
class IndexController extends \Phalcon\Mvc\Controller
{
include "/db.php"; // $db is initialized there
include_once ("Smarty.class.php");
$main_smarty = new Smarty;
public function indexAction()
{
function doSearch($limit) {
global $db, $current_user, $main_smarty; // db and smarty objects
$db->get_results("// my query"");
$search_clause = $this->get_search_clause();
$main_smarty->assign('search', $this->searchTerm);
}
}
}
Fatal error: Call to a member function get_results() on a non-object
But it works fine in my original code, without Phalcon.
include "/db.php";
function doSearch($limit) {
global $db, $current_user, $main_smarty;
$search_clause = $this->get_search_clause();
$main_smarty->assign('search', $this->searchTerm);
My bootstrap (Index.php) File:
try {
//Register an autoloader
$loader = new \Phalcon\Loader();
$loader->registerDirs(array(
'Controllers/',
))->register();
// DI
$di = new Phalcon\DI\FactoryDefault();
// View component
$di->set('view', function(){
$view = new \Phalcon\Mvc\View();
$view->setViewsDir('/');
return $view;
});
$application = new \Phalcon\Mvc\Application($di);
echo $application->handle()->getContent();
} catch(\Phalcon\Exception $e) {
echo "PhalconException: ", $e->getMessage();
}

Woah there, hold on a second..
It is great that you've identified the code you've written could be improved, but improvement doesn't just mean "moving everything from one place to another".
What you have the chance to do is to rethink how you've done things, and move into a "more structured" (in my opinion..) way of working.
Assuming you have successfully got Phalcon setup, I'd suggest reading through the first tutorial. This really is a great resource.
The MVC architecture is something a lot of sites run with nowadays, and if you aren't familiar with it, this link might help you out.
It is difficult to provide a direct answer to your question, as I think after a bit of reading everything will become clearer.
In your case, it sounds like it might be good to have this Smarty class as a service, that can be registered with Phalcon that can then be dependency injected.
Have a go/read, and let me/us know how it goes for you.

Related

Creating an MVC using good practices, and making it testable

I recently asked my girlfriend to teach me how to create a website using PHP and if possible to make me understand frameworks. “Of course” she replied helpfully, and gave me some links and books to help me understand the basics of how it works, and what PHP is.
In my web-grazing I visited many sites, including this one (which appeared whenever I had a serious question not relating to cats) and learned about many things including, but not limited to, classes, autoloading, database connections, evil singletons, and design patterns.. not all of which I totally grasped, but at least I wasn’t like ’uh, what?’ when she mentioned them.
We then sat down and she walked me through the getting started phase of creating my own MVC framework.
We created the following folder structure: (abridged):
app/core/
app/controllers/template/
and routed all requests via index.php, to the router class and on the way loaded an autoload class that registered the classes we will use on the site (currently only Template - in controllers/template/template.php - which only echos ‘Hello Rufferto’)
It was at this point that she told me that she liked to create a router table in the database, rather than keeping the routing info in a file, and said:
“let’s instantiate a database class in the router class where we will read the table info. I have my database class already in the app/core directory, It’s got all the mysql_connect() stuff already, and we only ever need one connection, so we’ll load it here as a singleton."
I of course immediately grabbed all her stuff, and threw it and her out into the street, shouting "Don't come back you evil harridan! I think the people at stackoverflow will have to take it from here’
This does leave me with a small problem. Everything we had done up to that point was now being called into question, and before I go any further with my plan to create an MVC framework that will end world hunger, create peace throughout the land, and make anyone who helps me with this question totally irresistible to every member of the gender they prefer, I want to make sure I haven’t started off with all the wrong ingredients.
I have used my most newly acquired skill - pasting - to put the code so far below, and wish some help with the black clouds hanging over the code, which I have put, as I see them, below said code....
index.php:
<?php
require_once(__DIR__ . '/app/config.php');
\APP\Core\Router::create();
config.php:
require_once 'core/autoloader.php';
autoloader.php:
<?php
namespace APP\Core;
class Autoloader
{
public static $loader;
public static $namespaces = array(
'APP\Core' => array('app/core'),
'APP\Models' => array('app/models'),
'APP\Controllers' => array('app/controllers')
);
public static function init()
{
if (self::$loader == null) {
self::$loader = new self();
}
return self::$loader;
}
public function __construct()
{
spl_autoload_register(function ($class_name) {
foreach (Autoloader::$namespaces as $current_namespace => $paths) {
//iterate through each namespace and add the classes - code removed for brevity - it working fine!
}
});
}
}
Autoloader::init();
router.php:
<?php
namespace APP\Core;
class Router
{
function __construct()
{
// crazy ex girfriend instantiated databse here, ive moved it since i want to use PDO
}
public static function create($path='')
{
$cl=get_called_class();
$class = new $cl;
if(empty($path)) $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$query = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
$db_opt = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$db = new \PDO(STD_PDO_DSN, DB_USER, DB_PASSWD, $db_opt);
$sql = "SELECT * FROM `router` where (regex=1 and ? REGEXP `source`) or (regex=0 and `source` = ?) order by `order` asc";
$stmt = $db->prepare($sql);
$stmt->execute([$path, $path]);
$redirect = $stmt->fetch();
if($redirect){
// code here just makes params array from the table - stuff like a body class, meta title etc
$class_redirect::create($params)->run();
}
}else{
echo 'you're an idiot, and should just flip burgers instead';
}
}
}
template.php:
<?php
namespace APP\Controllers\Template;
class Template
{
public function process_some_action()
{
// Like dealing with a post containing new baby names
}
public function process_default()
{
// Do incredible default stuff like list baby names
}
public static function create($params=array())
{
$class = get_called_class();
return new $class($params);
}
function run($params=array())
{
$processed=false;
$processed=$this->call_if_exists("process_some_action");
if(!$processed){
$processed=$this->call_if_exists("process_default");
}
if($processed){
$this->view();
} else {
echo 'Oh ffs Groo';
}
}
function call_if_exists($method_name)
{
if(method_exists($this, $method_name)){
$this->$method_name();
return true;
} else {
return false;
}
}
function view()
{
echo '<br/>';
echo 'Hello Rufferto!';
echo '<br/>';
}
}
1: am I calling the autoloader correctly - is it ok to put it in the ‘config’ file, or should I be putting it somewhere else?
2: er, is it a singleton? If so how can I fix that?
3: I want to use PDO, and have seen a couple of posts directing the questioner to ‘get rid of the useless database class…. But surely what I am looking for here is a ‘class’ that contains the code to make a connection, and have all the controllers or models that need it just call it (and if it doesn’t exist already create it)?
4: I am not convinced I understand the point of the 'create()' function in template (or router for that matter)....
Of course there may be other things wrong, and feel free to point them out to me, but these 4 things are the root of my issue, as I have been going round in circles, renaming things, getting into a state because I’m no longer sure if things are singletons, calling classes that wrap the PDO stuff, then scrapping it all and starting again.
No, there’s no models or views …. I’m not there yet (I will almost certainly use xslt unless the internet breaks with all the stackers shouting at me that it’s moronic) and 1 last thing…… I am not yet comfortable enough with my understanding of dependency injection to use it in any sentence other than this one.
P.S. Absolutely the 1 thing I will never do is call the ex and tell her I’m stuck…….
P.P.S If you have got this far, here’s a treat: http://img1.joyreactor.com/pics/post/comics-threepanelsoul-programmer-job-653145.png
This question would be better suited for codereview. SO is more for "this code doesn't work, what did I do wrong?" questions, rather than "this code works, but can it be improved or am I even doing the right thing?" type questions.
Having said that:
Usually, config files are just that: files containing configuration details. Think database credentials, API keys for third party resources, that sort of thing. You should NOT instantiate classes there. Most full frameworks include a Bootstrap class that handles that sort of thing: initialising the application, loading the config file, autoloader and routes, etc.
Your autoloader is currently a static class with only static methods. That's one way of making a singleton. There's nothing really wrong with that, because you'll only ever want one autoloader in your application.
I suggest you look into some ORM-type libraries, like Doctrine. I've just started work on a project that uses CakePHP's ORM library. If you want to build it yourself, I recommend writing an abstract base model class that has all the logic for retrieving, saving and deleting entries from the database, where each model class itself has a few properties that are unique per model (such as the database table name, for example) and extends this base model.
Those ::create() functions would be useful in a singleton, where they return the existing object if it has already run or instantiate a new one if not, (see example below). Since you seem to have a dislike for singletons, you might want to think about dropping the create() method and just doing the initial setup in the constructor.
Additionally, I wouldn't recommend XSLT for templating. It's a pain, because you'll have to make sure all your controllers build an XML file for your content. If you're going to use templates, look into existing template engines like Twig or Smarty.
Here's that example I promised about using a create() function for a singleton:
class Template {
private static $instance;
public static function create($params = array()) {
if (self::$instance !== null) {
return self::$instance;
}
self::$instance = new static($params);
// do additional setup for the new instance here
return self::$instance;
}
}
Although I'd personally use the name get() instead of create().

Init another class in construct method?

I am looking into trying to simplify my PHP code some more, and I have yet to find an answer with this methodology one of my team members are using. Nor, have I ever saw this done before anywhere on the web.
Here is the code example from our web application which he is working on with me.
<?php
class ArticlesHandler {
public function __construct() {
require 'Articles.php';
$articles = new Articles;
}
}
?>
Is this proper to init one class within another class?
For me, this just seems not proper standard to init classes to work together.
Yes and no. It works, but this particular code can lead to a number of problems.
You should be using require_once instead of require to avoid possible errors of including the same file twice. As it is this code here will bring your app to a complete stop:
new ArticlesHandler;
new ArticlesHandler;
This creates a hard coupling to the Articles class. You should probably rather be using dependency injection and pass an instance of Article to the constructor of ArticlesHandler. See How Not To Kill Your Testability Using Statics.
Yes, it is proper and normal to call constructors in a constructor. There is nothing weird/bad about it.
This is what I normally do.
class Repository {
protected $_models = array();
public function getModel($model, array $params = array()){
require_once $model.'.php'; //Replace this with an autoloader
if(empty($this->_models[$model])){
if(!empty($params)){
$this->_models[$model] = new $model($params);
} else {
$this->_models[$model] = new $model();
}
}
return $this->_models[$model];
}
}
And call the other class like this.
class ArticlesHandler extends Repository {
public function __construct() {
$articles = $this->getModel('Articles');
}
}
it seem's right.
For me, this just seems not proper standard to init classes to work together.
you can extend Articles class if you want to use Articles class inside the ArticlesHandler

Initializing View, Template and Controller and Model is optional

I was too sleepy when I asked the question, so sorry for that, anyway to make things clear I prepared the question for 2 hours.
I'm trying to organize my code and decided to organize it mvc'ish(mvc-like), I don't know if I can follow all the principles, but I wanted to be at least close to that.
My application has a front-controller (dunno if my definition is right), so that all the http-request of my application will be passing through a single point, in my case the index.php in the root directory of my application.
Having said that I have set it up like that, you can imagine that I used .htaccess to direct all request to index.php.
I exploded the url and created an array out of it, $url[] like so. So whenever I access my app like this http://localhost/app/pagename it'll be accessing a controller (pagename_controller)
I did it like this :
$file = $controller_path . $page . '_controller.php';
if (file_exists($file)) {
require $file;
$class_name = ucfirst($page) . '_controller';
$target = new $class_name();
}
also I wrap it up in a Container, the 'decorator pattern', for future use, validations maybe.
like this :
$controller = new Wrap($target);
$controller->index();
I don't know if the use of $controller variable name is appropriate so please forgive me when it is all wrong.
I kinda think that I can setup my application like this :
user sends a request, how? by using the application means that he/she sends out a http-request, that will load the initial state of the application
As you can see in the diagram of my desired application structure, I was able to do only the first part which is to direct the request to a single entry (index.php)
Now the problems are the initialization of other parts of the application.
As of this moment, I have 3 files that I want to setup, but I am confused on how.
index_controller, index_view, Template
class Index_controller {
private $model;
private $view;
public function __construct(){
// optional model -> $this->model = 'index'
$this->view = 'index' //
}
public function index(){
$this->load->view($this->view)
}
}
class Index_view {
private $model;
private $template;
public function __construct(Model $model = null){
$this->template = new Template('default');
}
public function view() {
$this->template->assign('css', 'default_css'); // don't know if this is efficient
// or $this->template->assign('header', 'default_header');
// or $this->template->assign('sidebar', 'default_sidebar');
// or $this->template->assign('footer', 'default_footer');
// or any other things I want to use in the template
}
}
class Template {
public $data = array();
private $tmpl;
public function __construct($template) {
$this->tmpl = $template . '_tmpl.php';
}
public function assign($name, $value){
$this->data[$name] = $value;
}
// public function output
// function that will explode the data array and render it out as a webpage
// I'll create templates and
}
With that at hand, I want to know now how do I link those things together. At the moment I have a system folder that can contain classes, and I setup a autoloader for that folder.
I am thinking of creating a Controller class and View class that acts as the ActionFactory and ViewFactory as illustrated in the diagram, although I know that these are not their responsibilities.
I am thinking of this :
class Controller {
protected $load;
public function __construct() {
$this->load = new View();
}
}
class View {
public function __construct() {
// some things i don't know
}
public function view() {
// some things i don't know
}
}
What are your suggestions and comments in my setup. How can I initiate the triad?
Well, let's not get stuck on your implementation details too much. You can go read about securing your framework at some other time. Let's deal with your question...
I actually created a framework that works along the lines you are trying to implement. I think what you are missing is a RoutingHandler class. Routing is the physical manipulation of the URL, which tells your application which Controller to load, and which Action to run.
In my world I also have Modules, so the basic routing scheme is
Module -> Controller -> Action
These three items map to my URI scheme in that fashion. Variables can be appended also like so...
http://www.domain.com/module/controller/action/var1/val1/var2/val2
So, what happens after the URI is parsed, and control is passed over to the appropriate controller and action? Let's make some code up to demonstrate a simple example...
<?php
class indexController extends Controller {
protected function Initialize() {
$this->objHomeModel = new HomeModel;
$this->objHeader = new Header();
$this->objFooter = new Footer();
$this->objHeader
->SetPageId('home');
}
public function indexAction() {
$this->objHeader->SetPageTitle('This is my page title.');
}
}
?>
In the Initialize method, I'm setting some controller-wide stuff, and grabbing an instance of my Model to use later. The real meat is in the indexAction method. This is where you would set up stuff to use in your View. For example...
public function randomAction() {
$this->_CONTROL->Append($intSomeVar, 42);
}
_CONTROL is an array of values that I manipulate and pass onto the View. The Controller class knows how to find the right template for the View because it is named after the Action (and in a sibling directory).
The Controller parent class takes the name of the action method and parses it like so...
indexAction -> index.tpl.php
You can also do some other fun stuff here, for example...
Application::SetNoRender();
...would tell the Controller not to render inside a template, but just complete the method. This is useful for those situations where you don't actually want to output anything.
Lastly, all of the controllers, models, and views live inside their own directory like so...
my_module
controllers
indexController.class.php
someotherController.class.php
:
:
models
HomeModel.class.php
:
:
templates
index.tpl.php
someother.tpl.php
:
:
I could go on, but I'm writing this from memory, and there are some wrinkles here and there, but hopefully this gives you food for thought.

replicating a feature in zend

I have been pouring over Zend_View and the various classes and interfaces that make up the view. One thing I am attempting to replicate in a project that does not use zend in any way shape or form is the:
$this->view->variable = 'Hello world';
that you can set in a controller and then do:
echo $this->view->variable;
My ultimate goal is to do something like:
$this->variable = new SomeClass
and then else where, in a view specifically, do:
$this->variable->someMethod();
My question is:
How would I replicate what zend does to do something simmilar with out using global variables?
How is zend able to do something like $this->view with out ever instantiating or saying what view is?
this would help me understand how, variables are passed around or objects are passed from the logic to the view and how php allows for something like $this->view to work when in a view or not.
note: this is not a Zend specific question and "use zend" is not the answer. I am looking to replicate a specific feature. My project does not in any way use or affiliate with zend.
I don't know why exactly you want to achieve this, but as a super simple setup (which is by no means suited to be the basis of an MVC framework) you can look at this:
<?php
class Controller
{
private $view = null;
public function __construct()
{
$this->view = new View();
$this->view->someVar = "foobar";
}
public function render()
{
include "view.php";
}
}
class View
{
}
$controller = new Controller();
$controller->render();
And then, in view.php, you can do:
<?php
echo $this->view->someVar;
Beware: This code only shows HOW it's possible to achieve such a construct, it does not anything useful at all ;).
It's actually pretty simple. When you use include, require, eval et al., the loaded code is brought in to the current scope. So if you have a template file
template.php
<span><?=$this->view->somevar?></span>
Controller.php
<?php
class Controller
{
private $view;
public function doSomething()
{
$this->view->somevar = 'Hello World';
include 'template.php';
}
}
index.php
<?php
require 'Controller.php';
$oC = new Controller();
$oC->doSomething();
Blamo.., template.php is able to call $this->view->somevar as it is treated as part of Controller.php. Running php index.php on the CLI produces
<span>Hello World</span>
To elaborate a tiny bit, if $this->view inside of Controller.php were a class you've defined rather than a simple instance of stdClass as in the above demonstration, and it had a function someMethod, you could call $this->view->someMethod() from template.php just the same.

Can spl_autoload be placed in another file?

I'm currently making my first website with PHP. Rather than writing autoload for each individual page, I wish to create one file with a general autoload ability.
Here is my autoloadControl.php:
// nullify any existing autoloads
spl_autoload_register(null,false);
//specify extensions that may be loaded
spl_autoload_extensions('.php. .class.php');
function regularLoader($className){
$file = $className.'.php';
include $file;
}
//register the loader function
spl_autoload_register('regularLoader');
Here is my index.php file:
require("header.php");
require("autoloadControl.php");
$dbConnection = new dbControl();
$row=$dbConnection->getLatestEntry();
Currently, the $dbConnection = new dbControl() gives me the following error:
Fatal error: Class 'dbControl'
So my question is, is there a way to use autoload this way or must I place it at the top of every PHP file I write that uses another file?
Placing spl_autoload in an external file is both valid and a good practice for making your code more maintainable--change in one place what could be 10, 20, or more.
It appears that your dbControl class is not being provided in the code you provided. Assuming you are including the class before referencing it, and the class works properly, then you should have no problem accomplishing this task.
require("header.php");
require("autoloadControl.php");
$dbConnection = new dbControl(); // Where is this class located?
Here is an OOP approach for your autoloadControl.php file:
<?php
class Loader
{
public static function registerAutoload()
{
return spl_autoload_register(array(__CLASS__, 'includeClass'));
}
public static function unregisterAutoload()
{
return spl_autoload_unregister(array(__CLASS__, 'includeClass'));
}
public static function registerExtensions()
{
return spl_autoload_extensions('.php. .class.php');
}
public static function includeClass($class)
{
require(PATH . '/' . strtr($class, '_\\', '//') . '.php');
}
}
?>
Your problem is not related to where you are defining your callback, but how.
Using spl_autoload_extensions('.php') would achieve the same thing as your custom callback; you don't need both if your callback is as simple as this. Your comment is also wrong - calling spl_autoload_register with no arguments will not clear current callbacks, but it will register the default callback.
However, in your code, you have specified the argument to spl_autoload_extensions incorrectly - it should be a comma-separated list of extensions. So I think what you want is this:
// Tell default autoloader to look for class_name.php and class_name.class.php
spl_autoload_extensions('.php,.class.php')
// Register default autoloader
spl_autoload_register();
// READY!
The main difference this will make from your code is that the default autoloader will look for 'dbcontrol.php' (all lower-case) whereas yours will look for 'dbControl.php' (case as mentioned in PHP code). Either way, you certainly don't need both.

Categories