I'm taking an application I wrote, a form, and rewriting it in Silex. It was, for various reasons, written like:
page1.php
page1_process.php
page2.php
page2_process.php
page3.php
page3_process.php
Where pageX.php is an HTML form with little PHP code, that passes data to the process script. The process script stores data to a Form object, and serializes the object in the Session.
Currently, I have the pageX.php pages as TWIG template files and they work, but I don't know how to incorporate the logic of the procedural process scripts.
I'm sure I don't copy the actual logic to the controllers.
If I follow your requirements, you just need the same route twice: one for get (displaying the form) and one for post to handle it. In the post controller you just need to include your pageX_process.php and you should be ready to go.
Translated in Silex code it should be something like:
<?php
/**
* if you're using silex-skeleton
* from: https://packagist.org/packages/fabpot/silex-skeleton)
* this file should be src/controllers
*/
// standard setup, like error handling and other route declarations
$app->get('/page1', function() use ($app) {
// you're currently using this (somehow)
$params = []; //set up your template parameters here
return $app['twig']->render('page1.twig', $params);
});
$app->post('/page1_proccess', function() use($app) {
ob_start();
require PATH_TO_LEGACY_FILES_DIR . '/page1_process.php';
return ob_get_clean();
});
From now on, and if you want / find it adequate, you can start to refactor your pageX_process.php pages in a more OOP / Silex way, but you have a starting point with this application architecture.
NOTICE:
you should move your php files away from the web directory (for example legacy/ in the root of your project)
you must point your form handling script (the action parameter) to the new route (you can make it to work using the old route also but requires some little more effort)
Related
Hi i'm pretty new on create endpoints in php.
Now i have to create a little endpoint to intercept some updates from electronic invoice service.
From admin panel of the service i can specify endpoint url where my application is located.
For example I indicate: www.example.com/api/endpoint/index.php
NOte: If I set only www.example.com/api/endpoint/, panel admin tell me there is an error - page not found
Now in the admin panel I see I can intercept these POST methods:
/createInvoice
/createNotification
But i don't undertand HOW to differentiate there 2 methods...
Actually in my index.php i've:
<?php
function call_create_invoice(
...
);
function call_create_notification(
...
);
header('Content-Type: application/json; charset=utf-8');
var_dump($_POST);
If I use POSTMAN to do some test using POST call, i can correctly see $_POST parameters sent.... but i don't understand how to:
call call_create_invoice function if /createInvoice is called
call call_create_notification function if /createNotification is called
If you would stick to vanilla PHP, you need to parse the request URI to call the desired function like:
$path = $SERVER['REQUEST_URI'];
switch($path) {
case('/createInvoice'):
call_create_invoice();
break;
case('/createNotification'):
call_create_notification();
break;
}
As requirements grow, it may make sense to use a minimal PHP framework with routing functionality, such as Laravel Lumen.
I am writing my own php mvc framework (just for training). The question is how to handle exception when the requested controller doesn't exist? Should I call 404 class or create and show new View from Router? I'll be glad if you have any advices for me!
Here are my autoload.php:
function __autoload($class)
{
$filename = __DIR__ . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
if (file_exists($filename))
{
require $filename;
}
else
{
throw new \Exception('The file doesn\'t exists!');
}
}
and Route.php:
namespace App;
class Route
{
public static function start ()
{
$controller_name = 'News';
$controller_action = 'Index';
if (isset($_GET['furl']))
{
// Getting rid of spaces
$url = str_replace(' ', '', $_GET['furl']);
if (substr($url, -1) == '/')
{
$url = substr($url, 0, count($url) - 2);
}
$arr = explode('/', $url);
foreach($arr as &$value)
{
$value = strtolower($value);
$value = ucfirst($value);
}
$controller_action = $arr[count($arr) - 1];
unset($arr[count($arr) - 1]);
$controller_name = implode('\\', $arr);
}
$controller_name = '\App\Controllers\\' . $controller_name;
try
{
$controller = new $controller_name();
}
catch (\Exception $e)
{
//HELP ME PLS!
}
$controller->action($controller_action);
}
}
No matter how many web frameworks, routers, autoloaders, etc are there already: keep doing what you think it's right for you and suitable to your momentarily understanding level, in order to LEARN. Actually, by confronting yourself with problems arised along the process of implementing yourself different parts of your application, you will not only gain the opportunity to learn and discover new things, but also to learn how and what to study in the already existing frameworks' design.
Study the PHP Standard Recommendations (the ones marked as "accepted"). Especially PSR-1,2,4,7. They are used by many frameworks and PHP projects. Read FAQs to find out more about the project itself.
Autoloader:
The PSR-4 provides a link with examples at the document end.
#mike suggested, that you should use the Composer autoloader. I agree with him and I strongly recommend it to you too. BUT I suggest you to do this only after you correctly implement and make use of your own autoloader (PSR-4 conform). Why? You definitely need to learn how the autoloading process works. And in some future situations you will still need your own autoloader implementation, even after Composer is installed and running.
Also be aware that you must not raise any exceptions from autoloader itself!
Router:
Btw, your class should be called "Router".
The router should not be responsible for validating the controller class/file and the action, nor for calling the action. These tasks are part of the "front controller" responsibilities. Your router should just return the components resulted after parsing, e.g. "exploding" the request URI ($_GET['furl']), in some form (as a Route object (with them as properties), as array, etc). These components are the controller name, the action name, the action parameters list (NB: the action parameters are not the query string parameters). The front controller uses them further to validate/access the controller class/file and its action and to call the action.
But please note that a router works actually in other way. In short: it matches (e.g. compares) the request method (GET, POST, etc) and the request URI against an existing (e.g. predefined by you) list of route definitions. A route definition contains the infos related to a specific controller, action, etc. If the HTTP method and the request URI "correspond" to one of the route definitions, then the router returns the matched definition's components to the front controller (in some form: as object, as array, etc).
For more details describing this principle see:
How to load classes based on pretty URLs in MVC-like page?
FastRoute
Aura.Router
Front controller:
It can be a class, but it can also be just vanilla code in the entry point of your app (index.php, bootstrap.php, etc). In the latter case, the front controller code should reside in a file outside of the document root of the app. For example in a bootstrap.php file, which is to be just included in index.php - whereas index.php resides inside the document root.
"controller/action not found" specific handling:
If a controller, or an action is not found/valid, then call a predefined action (for example displayError) of a predefined Error controller, which informs the user that, for a specific part of his request (actually of his provided request URI), no resource was found.
For example, consider that the user provided the request URI www.example.com/books/show/12. Conform to your app workflow the controller is Book, the action (e.g. the controller's method) is show and the action parameter is 12 (the value is passed as argument to the show method and defined as $bookId parameter in it). But, if the controller class is not defined, or no controller file exists, then the front controller should call the action displayError of Error controller, which should display a message like No resource found for your 'book' request. A similar info should be displayed when the show method is not yet defined in the Book controller.
Note that, if the Error controller or its action is not found/valid, then the PHP engine raises a corresponding error/exception. If you follow the next links I provided, you'll end up implementing three custom error/exception handling functions (referenced by set_error_handler, set_exception_handler and register_shutdown_function, respectively). They will catch and handle the described situation properly.
To read: Manage the errors of a framework
General error/exception handling in MVC:
Here are some good resources:
Again: Manage the errors of a framework
Error logging, in a smooth way
Error reporting basics
The (im)proper use of try..catch
Other MVC related resources:
Build a PHP MVC Application (Just for the start...)
Dependency Injection and Dependency Inversion in PHP
MVC for advanced PHP developers (A further list of resources)
Tom Butler's Programming Blog. MVC, PHP, Best practices
Clean, high quality code
P.S: Avoid the use of statics, globals, singletons. Why? Read here and here, for example.
Good luck.
I need to run a php script once before my zend framework application is bootstrapped and run. This works now by calling this initial script in my /public/index.php, however the script is run for all subsequent zend framework page requests as well. I need this script to only be run once on the initial request and not again when additional pages are loaded via ajax.
My initial attempts were to set some php constants in the /public/index.php file like so:
if (!defined('SOME_VAR')) require_once 'path/to/script/to/run/once.php';
define('SOME_VAR', '1);
However, when another page is loaded via ajax, even though I've defined 'SOME_VAR', it doesn't persist and stay as defined and the script is executed again.
I'm using Zend Framework 1.11, Apache (Xampp).
Executing this script AFTER Zend has been bootstrapped and run (inside Zend Framework) is not an option.
Constants don't work because they only exist for the duration of the request.
It sounds like you want to figure out if the current request is a regular request or an XmlHttpRequest (AJAX) request.
You could try something like this at the top of your index.php:
<?php
define('IS_AJAX_REQUEST', isset($_SERVER['HTTP_X_REQUESTED_WITH'])
&& strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
if (! IS_AJAX_REQUEST){
// run your code.
}
If you're using sessions, you could also just set a flag in the session once you special code has been run, and test for that.
According to the Zend Framework Documentation:
However, should custom initialization be necessary, you have two choices. First, you can write methods prefixed with _init to specify discrete code to bootstrap. These methods will be called by bootstrap(), and can also be called as if they were public methods: bootstrap(). They should accept an optional array of options.
If your resource method returns a value, it will be stored in a container in the bootstrap. This can be useful when different resources need to interact (such as one resource injecting itself into another). The method getResource() can then be used to retrieve those values.
The other option is to use resource plugins. Resource plugins are objects that perform specific initializations, and may be specified:
When instantiating the Zend_Application object
During initialization of the bootstrap object
By explicitly enabling them via method calls to the bootstrap object
Resource plugins implement Zend_Application_Resource_ResourceAbstract, which defines simply that they allow injection of the caller and options, and that they have an init() method.
I have a project based on codeigniter. And I should use one class that extended from a codeigniter controller in another php file. But I didn't find the solution about how to teach another php file to see whole CI-project. Beyond that needed class can not inherit when i call it from other place.
I'm not 100% sure if this helps get you in the right direction, but kudos if it does!
Codeigniter routes the application depending on the environment state of the URI. What you need to do is set the environment and include the index view file like so:
$_SERVER["REQUEST_URI"] = "cms/2";
//Set GET action,method params etc
require_once "path/to/index.php";
When you load CI Index file it reads the SERVER Variable and others which you may have to find and execute the controller and method, I would also advise that you modify the library/view file as it may exit upon output causing your script to exit.
Also you may wis hto look into ob_start() to catch the buffer.
I'm working on converting my standard PHP project to OOP but I ran into a wall about how to handle AJAX calls with PHP Classes. I'm not happy with the way I'm doing this now. I have a TillAjax.php file which I call from my TillUI.php file from a AJAX call.
In the TillAjax.php file I do this to get the information passed from the ajax call.
$till = new Till();
if(isset($_POST['data']))
$till->doStuff($_POST['data']);
I think this ruins the OOP.
I have worked with ASP.NET MVC and here its possible to call a specific action in a controller without i have to check for the post value. So I want to know if there is a smarter PHP way to solve the above problem?
The method I use for this is to have an Ajax class.
Your php file calls Ajax::Process($_GET['handle']), where 'handle' contains the name of a static class method, so perhaps 'Till::Process'. The Ajax class checks the function against a list of permitted functions (i.e. functions that you are allowing to be called via ajax), and then uses call_user_func_array to call the function (my code uses the contents of $_POST as arguments to pass to the function). The return of that function is automatically encoded as json and outputted to the client.
This means that your target php file looks like this:
<?php
//File: ajax.php
include ("Ajax.php");
Ajax::Process($_GET['handle']);
?>
Which I think is pretty simple.
Then you can have javascript that looks like this (jquery) :
$.get('ajax.php?handle=Till::Process', {}, function(result) {
//called on page response
});
So then result now contains whatever data is returned from the php method Till::Process.
Have you considered using a PHP MVC framework such as CodeIgniter, CakePHP, Kohana, etc? They will let you route requests to specific controller methods. It will be a much cleaner solution if migrating to one of these frameworks is an option for you.