My first question is basically asking for a code-review. Does the code I'm about to provide use a Factory to promote Polymorphism? Its written in PHP. Here are the basic requirements:
Pass a long url to a library and
return a shortened url. Along with the
long url, pass user properties to
attempt to locate the users specific
shortener service and API key.
Allow users to set API keys for specific URL shorteners. My code assumes this is already set in the database and Bitly is the only service supported.
If a user doesn't have an API key and service set, use the default API key and service. Again, my code assumes the default is set to Bitly in the database.
If the url shortener service fails, log the failure, but don't throw an exception. The library should silently fail. Instead of using the short url, we'll use the long url.
Here is an example of calling the code:
<?php
$long_url = 'http://www.news.com/story/1';
$account_id = 1;
$use_this_url = $long_url;
$meta = array(
'account_id' => $account_id,
// OPTIONS
// 'service_id' => $service_id,
// 'account_id' => $account_id,
);
$shortener = new Shortener_Factory($long_url, $meta);
if ($shortener->shorten_long_url() AND $shortener->save_short_url())
{
$use_this_url = $shortener->short_url;
}
echo $use_this_url;
Here are the classes:
<?php
interface ShortenerServiceInterface {
public function save_short_url();
public function shorten_long_url();
}
abstract class ShortenerServiceAbstract implements ShortenerServiceInterface {
// Url to be shortened
public $long_url = '';
// Long url unique id
public $url_id = 0;
// Service unique id
public $shorturlservice_id = 0;
// Service account unique id
public $shorturlserviceaccount_id = 0;
// Short url service unique API login
public $api_login = '';
// Short url service unique API key
public $api_key = '';
// Short url service unique hash which maps to original url value
public $hash = '';
// Shortened url string
public $short_url = '';
// Attempt to call shortner service three times before failing
public $attempts = 3;
// Shorten long url with specific service API/logic
public function shorten_long_url()
{
// Can't save a short url when one doesn't exist
if (!$this->long_url OR !$this->api_login OR !$this->api_key) {
log('error', 'ShortenerServiceAbstract::shorten_long_url - no long url to shorten - '.var_export($this, TRUE));
return FALSE;
}
}
// Save short url and related meta-data to shorturls table
public function save_short_url()
{
// Can't save a short url when one doesn't exist
if (!$this->url_id OR !$this->hash OR !$this->shorturlservice_id OR !$this->shorturlserviceaccount_id) {
log('error', 'ShortenerServiceAbstract::save_short_url - no short url to save - '.var_export($this, TRUE));
return FALSE;
}
// Insert a new short url, or update an existing record
$saved = Shorturl_Model::insert_on_dup_key_update($this->url_id, $this->hash, $this->shorturlservice_id, $this->shorturlserviceaccount_id);
if (!$saved) {
log('error', 'ShortenerServiceAbstract::save_short_url - short url record can not be saved - '.var_export($this, TRUE));
return FALSE;
} else {
return TRUE;
}
}
}
// Bitly, a simple url shortener
// #link http://code.google.com/p/bitly-api/wiki/ApiDocumentation
class ShortenerServiceBitly extends ShortenerServiceAbstract {
public function shorten_long_url()
{
// Make sure we have required members set
parent::shorten_long_url();
$urlencoded = urlencode($this->long_url);
$bitlyurl = 'http://api.bit.ly/shorten?version=2.0.1&longUrl='.$urlencoded.'&login='.$this->api_login.'&apiKey='.$this->api_key.'&history=1';
$attempts = 1;
while ($attempts <= 3) {
$json_result = file_get_contents($bitlyurl);
if ($json_result) {
// Return an assoc array
$json_decode = json_decode($json_result, TRUE);
if (is_array($json_decode) AND isset($json_decode['errorCode']) AND $json_decode['errorCode'] == 0) {
// Don't compare sent URL with returned URL
// Bitly removes invalid poritions of URLs
// The camparison might fail even though the URLs are the "same"
$shortened = current($json_decode['results']);
break;
} else {
log('error', 'ShortenerServiceBitly::shorten_long_url - bit.ly json decoded - '.var_export($json_decode, TRUE));
}
} else {
log('error', 'ShortenerServiceBitly::shorten_long_url - bit.ly http response - '.var_export($json_result, TRUE));
}
$attempts++;
}
if (isset($shortened)) {
$this->short_url = $shortened['shortUrl'];
$this->hash = $shortened['userHash'];
return TRUE;
} else {
return FALSE;
}
}
}
// Shortener Factory
class Shortener_Factory {
// Shortener service account parameter object
// #param object shortener account properties
private $_account;
// Shortener service object created by factory
//#param object shorterner service functions
private $_service;
// Url to be shortened
private $_long_url;
// Set url members, service parameter object and finally the service itself
public function __construct($long_url, $meta=array())
{
$this->_long_url = $long_url;
$this->_set_account($meta);
$this->_set_service();
}
// Set shortener service account parameter object
// #param $meta array determine parameters for the current service object
private function _set_account($meta=array())
{
$s = FALSE;
// Get shorturl service account
if (isset($meta['account_id'])) {
$s = Shorturlserviceaccount_Model::get_by_account_id($meta['account_id']);
} elseif (isset($meta['service_id'])) {
$s = Shorturlserviceaccount_Model::get_by_service_id($meta['service_id']);
}
// Account not found, lets use default
if (!$s) {
$s = Shorturlserviceaccount_Model::get_default();
}
// Must have a service to use
if ($s === FALSE) {
log('error', 'Shortener_Core::_set_account - _account not found - '.var_export($this, TRUE));
return FALSE;
} else {
$this->_account = $s;
}
}
// Use shortener service account parameter object to set shortener service object
private function _set_service()
{
switch ($this->_account->name) {
case 'bitly':
$this->_service = new ShortenerServiceBitly;
break;
default:
log('error', 'Shortener_Core::_set_service - _account not set - '.var_export($this, TRUE));
return FALSE;
}
$this->_service->long_url = $this->_long_url;
$this->_service->shorturlserviceaccount_id = $this->_account->id;
$this->_service->shorturlservice_id = $this->_account->shorturlservice_id;
$this->_service->api_login = $this->_account->api_login;
$this->_service->api_key = $this->_account->api_key;
}
// Public API for shortener service object methods
public function __call($name, $arguments)
{
if (!$this->_service) {
log('error', 'Shortener_Core::__call - _service not set - '.var_export($this, TRUE));
return FALSE;
}
return $this->_service->$name();
}
// Public API for shortener service object members
public function __get($name)
{
return ($this->_service->$name) ? $this->_service->$name : NULL;
}
}
The job of the factory pattern is to abstract away the creation of objects. The reason this is useful is because the way in which objects are created may not the same as just:
$instance = new Object();
any time you create it. For example if you first need to deal with loading an include file or you need to pick one of a few derived classes based on some parameter that is not known before runtime.
A factory can be as simple as something like:
function getInstance($objectType, $params)
{
if (!class_exists($objectType)) {
throw new Exception('Bad class');
}
$instance = new $objectType($params);
return $instance;
}
Or can be as complex as you like, but these are the basic rules to follow. check out the wikipedia article here for a PHP example
Related
I have a class that I use to display pages, and I set many class wide variables in the constructor. One of them is coming up null, even though I can see in the call to create the object that the argument is passed. Here is the constructor for the class:
public function __contruct(string $pageTitle, string $page, bool $csrfFlag, string $pageType) {
$this->pageTitle = $pageTitle;
$this->page = $page;
$this->csrfFlag = $csrfFlag;
// Validating page type passed
switch ($pageType) {
case "main":
case "profile":
case "admin":
$this->pageType = $pageType;
default:
throw new Exception("A page type variable was passed that is unknown. That variable was $pageType");
}
}
And here is the particular object creation call:
$display = new PageDisplay('Login', 'login.php', true, 'auth_page');
The problem I'm having is that the variable marked $page isn't being passed through (and I know that because I try and call a late function in the class that displays pages and when it comes to the line about displaying the actual page (in this case, login.php) it gives me this error:Failed opening '' for inclusion). Here is that function if you would like to see it:
// Ultimately builds the page to show the user
public function buildPage(bool $needsHeadTags) : void {
// Generate CSRF token if needed
if ($this->csrfFlag === true) { $csrfToken = hash("sha512", random_bytes(64)); }
// Get extra HTML
if ($needsHeadTags === true) { $extraHeadTags = $this->getHeadTags(); }
$headerHtml = $this->getHeader();
$pageTitle = $this->pageTitle;
// Show page
include_once $this->page; // where the error is thrown
}
But I can see that it is passed in the constructor. What am I doing wrong?
<?php
declare(strict_types=1);
class PageDisplay {
private $simplePageNav = true;
private $header = "";
private $pageTitle;
private $page;
private $csrfFlag;
private $pageType;
// Sets the variables the rest of the class will use
public function __contruct(string $pageTitle, string $page, bool $csrfFlag, string $pageType) {
$this->pageTitle = $pageTitle;
$this->page = $page;
$this->csrfFlag = $csrfFlag;
// Validating page type passed
switch ($pageType) {
case "main":
case "profile":
case "admin":
$this->pageType = $pageType;
default:
throw new Exception("A page type variable was passed that is unknown. That variable was $pageType");
}
}
public function getPage() : string {
return $this->page;
}
// If the function returns true, it is just a simple navigation to the page
public static function isPageNav(bool $getAllowed) : bool {
// Checking if GET parameters are allowed, then checking if the correct things are empty, then return boolean with what we find
if (!$getAllowed) {
$simplePageNav = (empty($_POST) && empty($_GET) && empty(file_get_contents("php://input"))) ? true : false;
} else {
$simplePageNav = (empty($_POST) && empty(file_get_contents("php://input"))) ? true : false;
}
return $simplePageNav;
}
// Gets what the navigation should be based on what type of page the user went to (general web, profile, admin, etc...)
private function getHeader() : string {
// Control statement to display website correctly
switch($this->pageType) {
case "auth_page":
return "";
break;
}
return "hard";
}
// Gets what the additional head tags should be based on what type of page the user went to (general web, profile, admin, etc...)
private function getHeadTags() : string {
// Control statement to display website correctly
switch($this->pageType) {
//
}
}
// Ultimately builds the page to show the user
public function buildPage(bool $needsHeadTags) : void {
// Generate CSRF token if needed
if ($this->csrfFlag === true) { $csrfToken = hash("sha512", random_bytes(64)); }
// Get extra HTML
if ($needsHeadTags === true) { $extraHeadTags = $this->getHeadTags(); }
$headerHtml = $this->getHeader();
$pageTitle = $this->pageTitle;
// Show page
include_once $this->page;
}
}
PHP has a number of "special" methods that can be added to classes that get automatically called at points during the class lifecycle. The __construct function is executed on object initialisation, and __destruct on object destruction, when there are no remaining references to the object or when the script or program ends. It's totally legal to create other class functions that also start with a double underscore, so PHP will not complain about this:
class PageDisplay {
// Sets the variables the rest of the class will use
public function __contruct(string $pageTitle, string $page, bool $csrfFlag, string $pageType) {
// code not executed unless $object->__contruct is called
}
}
but the code in the __contruct function will not be executed when a new PageDisplay object is created.
The fix is simple:
class PageDisplay {
// Sets the variables the rest of the class will use
public function __construct(string $pageTitle, string $page, bool $csrfFlag, string $pageType) {
// code executed on calling new PageDisplay('...')
}
}
I've developed an plugin where i make use of shortcodes. One of these shortcodes retrieves external information through communicate with an API.
The function that's called by the shortcode, communicates several times with the API. How could i cache all separate calls.
Let me try to explain it with some code
Example code
public function generateData($atts, $content = null, $tag = null){
// $this->API is a extended class to retrieve the API data
// example one calling the api
$data1 = $this->API->getData(array(
'teamlevel' => 'beginner'
));
$data2 = $this->API->getData(array(
'competition' => 'baseball'
));
}
add_shortcode('getdata', 'generateData');
class API {
public function getData($params){
// communicate with the api en return the data
}
}
Above code is working but without caching. Now i've tried to make use of wordpress get_transient and set_transient on the following way
public function generateData($atts, $content = null, $tag = null){
// $this->API is a extended class to retrieve the API data
// example one calling the api
// new chaining method cache
// every data call could be cached, i thought this could be based on shortcode name ($tag)
// set the shortcode name
$this->API->cacheShortcodeName = $tag; // getdata
$data1 = $this->API->cache()->getData(array(
'teamlevel' => 'beginner'
));
$data2 = $this->API->cache()->getData(array(
'competition' => 'baseball'
));
}
add_shortcode('getdata', 'generateData');
class API {
private $cacheDuration;
protected $cacheShortcodeName;
public function getData($params){
// before communication with the api check if there is cache with hasCache
// 1. $this->hasCache
// if result is true return cached data
// 2. return cached data
// else communicatie with the API en set new cache data
// 3. retrieve data from api en set new cached data
}
// default cache duration (1 day) in seconds
public function cache($cacheDuration = 86400){
$this->cacheDuration = $cacheDuration;
return $this;
}
public function hasCache(){
// check if there is cache
$cache = get_transient(md5($this->cacheShortcodeName));
if(!empty($cache)){
return true;
}
return false;
}
public function setCache($data){
set_transient(md5($this->cacheShortcodeName), $data, $this->cacheDuration);
}
public function getCache(){
$cache = get_transient(md5($this->cacheShortcodeName));
return $cache;
}
}
But the problem is when the cache is saved within the function setCache each time the shortcode name is set, and therefore i think its overriding itself. How could i store every call separately?
I'm trying to create a custom omnipay driver for a local gateway called creditguard.
For this gateway you need to post the data to the endpoint and get back a redirect url for the payment form.
My question is how do you post and get the response before making the purchase?
Edit:
Gateway.php
class Gateway extends AbstractGateway
{
public function getName()
{
return 'Creditguard';
}
public function getDefaultParameters()
{
return array();
}
public function getEndpoint()
{
return 'https://verifonetest.creditguard.co.il/xpo/Relay';
}
public function purchase(array $parameters = array())
{
return $this->createRequest('\Nirz\Creditguard\Message\PurchaseRequest', $parameters);
}
public function completePurchase(array $parameters = array())
{
return $this->createRequest('\Nirz\Creditguard\Message\CompletePurchaseRequest', $parameters);
}
}
PurchaseRequest.php
class PurchaseRequest extends AbstractRequest
{
protected $liveEndpoint = 'https://verifonetest.creditguard.co.il/xpo/Relay';
protected $testEndpoint = 'https://verifonetest.creditguard.co.il/xpo/Relay';
public function getData()
{
$this->validate('amount');
// Either the nodifyUrl or the returnUrl can be provided.
// The returnUrl is deprecated, as strictly this is a notifyUrl.
if (!$this->getNotifyUrl()) {
$this->validate('returnUrl');
}
$data = array();
$data['user'] = 'user';
$data['password'] = 'password';
$data['tid'] = '11111111';
$data['mid'] = '111111';
$data['amount'] = '20000';
$data['int_in'] = '<ashrait>
<request>
<version>1000</version>
<language>HEB</language>
<dateTime></dateTime>
<command>doDeal</command>
<doDeal>
<terminalNumber>'.$data['tid'].'</terminalNumber>
<mainTerminalNumber/>
<cardNo>CGMPI</cardNo>
<total>'.$data['amount'].'</total>
<transactionType>Debit</transactionType>
<creditType>RegularCredit</creditType>
<currency>ILS</currency>
<transactionCode>Phone</transactionCode>
<authNumber/>
<numberOfPayments/>
<firstPayment/>
<periodicalPayment/>
<validation>TxnSetup</validation>
<dealerNumber/>
<user>'. $data['user'] .'</user>
<mid>'.$data['mid'].'</mid>
<uniqueid>'.time().rand(100,1000).'</uniqueid>
<mpiValidation>autoComm</mpiValidation>
<email>someone#creditguard.co.il</email>
<clientIP/>
<customerData>
<userData1/>
<userData2/>
<userData3/>
<userData4/>
<userData5/>
<userData6/>
<userData7/>
<userData8/>
<userData9/>
<userData10/>
</customerData>
</doDeal>
</request>
</ashrait>';
return $data;
}
public function sendData($data)
{
// $httpResponse = $this->httpClient->post($this->getEndpoint(), null, $data);
return $this->response = new PurchaseResponse($this, $data);
}
public function getEndpoint()
{
return $this->getTestMode() ? $this->testEndpoint : $this->liveEndpoint;
}
}
PurchaseResponse.php
class PurchaseResponse extends AbstractResponse implements RedirectResponseInterface
{
public function isSuccessful()
{
return false;
}
public function isRedirect()
{
return true;
}
public function getRedirectUrl()
{
// return $this->getRequest()->getEndpoint().'?'.http_build_query($this->data);
return $this->getRequest()->data['mpiHostedPageUrl'];
// return isset($this->data['mpiHostedPageUrl']) ? $this->data['mpiHostedPageUrl'] : null;
}
public function getRedirectMethod()
{
return 'GET';
}
public function getRedirectData()
{
return [];
}
}
Not sure how to get the response's mpiHostedPageUrl so I can redirect to the correct url.
Assuming this is the payment gateway documentation in question.
You just go ahead and make the transaction request, the customer won't be charged as they'll have to authorise it on the next page by entering in their payment details.
The response of that transaction request contains an element mpiHostedPageUrl, which you can see on page 13 of that document, that contains the URL you need to get from the response to provide the redirect.
I know this is very old question. First you need to set 'mpiHostedPageUrl' in your getData method of PurchaseRequest.php and access in PurchaseResponse.php by using $this->data['mpiHostedPageUrl'].
HATEOAS (Hypermedia as the Engine of Application State) is a way to organize the response for REST application.
In HATEOAS world, the response JSON may contains all URL the client may need. Example, in github API, the response contain URL for accessing to repository, user, pull request...
So, I suggest you to call the gateway with the first POST request and then, according to the JSON response, call to provided URL which will represent the redirection.
Another solution could be to use a ZUUL gateway (in Spring-Boot) which will perform the redirection for you.
You can find a description here : https://spring.io/guides/gs/routing-and-filtering/
I found this great code snippet that works wonderfully during circumstances that change a user’s credentials during that user’s session. The hasCredential() method defaults to looking at the database rather than the user’s session - which meets my needs perfectly (as a user’s credentials are often changed programmatically during a user’s session). So I really need to keep this functionality.
However, for circumstances that have multiple credentials, either an OR circumstance: [[A, B]] or an AND circumstance: [A, B] the code snippet fails as it only checks for the one instance and does not read the yaml security file for AND OR instances.
I’m looking for help with how to tweak the code snippet to take into account AND OR yaml credential permissions. Here’s the code snippet:
public function hasCredential($permission_name)
{
//this overrides the default action (hasCredential) and instead of checking
//the user's session, it now checks the database directly.
if (!$this->isAuthenticated()) {
return false;
}
$gu = $this->getGuardUser();
$groups = $gu->getGroups();
$permissions = $gu->getPermissions();
$permission_names = array();
foreach($permissions as $permission) {
$permission_names[] = $permission->getName();
}
foreach($groups as $group) {
$group_permissions = $group->getPermissions();
foreach($group_permissions as $group_permission) {
$permission_names = array_merge($permission_names, array($group_permission->getName()));
}
}
$permission_names = array_unique($permission_names);
return (in_array($permission_name, $permission_names)) ? true : false;
}
EDIT:
I think I need to merge the above code with the original hasCredential() below but I'm struggling with the logic:
public function hasCredential($credentials, $useAnd = true)
{
if (null === $this->credentials)
{
return false;
}
if (!is_array($credentials))
{
return in_array($credentials, $this->credentials);
}
// now we assume that $credentials is an array
$test = false;
foreach ($credentials as $credential)
{
// recursively check the credential with a switched AND/OR mode
$test = $this->hasCredential($credential, $useAnd ? false : true);
if ($useAnd)
{
$test = $test ? false : true;
}
if ($test) // either passed one in OR mode or failed one in AND mode
{
break; // the matter is settled
}
}
if ($useAnd) // in AND mode we succeed if $test is false
{
$test = $test ? false : true;
}
return $test;
}
I think I found the correct implementation of the hasCredential method that overrides the original code found in the sfGuardSecurityUser Class. The code asks the underlying GuardUser model to reload all the permissions and groups from the database and adds theses credentials to the SecurityUser object before checking the credentials.
/**
* Returns whether or not the user has the given credential.
*
* EXTENDED to reload all permission so that changes take
* immediate effect and user does not have to log out
*
* #param string $credential The credential name
* #param boolean $useAnd Whether or not to use an AND condition
* #return boolean
*/
public function hasCredential($credential, $useAnd = true)
{
if (empty($credentials))
{
return true;
}
if (!$this->getGuardUser())
{
return false;
}
if ($this->getGuardUser()->getIsSuperAdmin())
{
return true;
}
if (!$this->isAuthenticated()) {
return false;
}
$gu = $this->getGuardUser();
$groups = $gu->getGroups();
$permissions = $gu->getPermissions();
$permission_names = array();
foreach($permissions as $permission) {
$permission_names[] = $permission->getName();
}
foreach($groups as $group) {
$group_permissions = $group->getPermissions();
foreach($group_permissions as $group_permission) {
$permission_names = array_merge($permission_names, array($group_permission->getName()));
}
}
$permission_names = array_unique($permission_names);
if (!is_array($credentials))
{
return in_array($credentials, $permission_names);
}
// now we assume that $credentials is an array
$test = false;
foreach ($credentials as $credential)
{
// recursively check the credential with a switched AND/OR mode
$test = $this->hasCredential($credential, $useAnd ? false : true);
if ($useAnd)
{
$test = $test ? false : true;
}
if ($test) // either passed one in OR mode or failed one in AND mode
{
break; // the matter is settled
}
}
if ($useAnd) // in AND mode we succeed if $test is false
{
$test = $test ? false : true;
}
return $test;
}
I have been reading a lot about how and why to use an MVC approach in an application. I have seen and understand examples of a Model, I have seen and understand examples of the View.... but I am STILL kind of fuzzy on the controller. I would really love to see a thorough enough example of a controller(s). (in PHP if possible, but any language will help)
Thank you.
PS: It would also be great if I could see an example of an index.php page, which decides which controller to use and how.
EDIT: I know what the job of the controller is, I just don't really understand how to accomplish this in OOP.
Request example
Put something like this in your index.php:
<?php
// Holds data like $baseUrl etc.
include 'config.php';
$requestUrl = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$requestString = substr($requestUrl, strlen($baseUrl));
$urlParams = explode('/', $requestString);
// TODO: Consider security (see comments)
$controllerName = ucfirst(array_shift($urlParams)).'Controller';
$actionName = strtolower(array_shift($urlParams)).'Action';
// Here you should probably gather the rest as params
// Call the action
$controller = new $controllerName;
$controller->$actionName();
Really basic, but you get the idea... (I also didn't take care of loading the controller class, but I guess that can be done either via autoloading or you know how to do it.)
Simple controller example (controllers/login.php):
<?php
class LoginController
{
function loginAction()
{
$username = $this->request->get('username');
$password = $this->request->get('password');
$this->loadModel('users');
if ($this->users->validate($username, $password))
{
$userData = $this->users->fetch($username);
AuthStorage::save($username, $userData);
$this->redirect('secret_area');
}
else
{
$this->view->message = 'Invalid login';
$this->view->render('error');
}
}
function logoutAction()
{
if (AuthStorage::logged())
{
AuthStorage::remove();
$this->redirect('index');
}
else
{
$this->view->message = 'You are not logged in.';
$this->view->render('error');
}
}
}
As you see, the controller takes care of the "flow" of the application - the so-called application logic. It does not take care about data storage and presentation. It rather gathers all the necessary data (depending on the current request) and assigns it to the view...
Note that this would not work with any framework I know, but I'm sure you know what the functions are supposed to do.
Imagine three screens in a UI, a screen where a user enters some search criteria, a screen where a list of summaries of matching records is displayed and a screen where, once a record is selected it is displayed for editing. There will be some logic relating to the initial search on the lines of
if search criteria are matched by no records
redisplay criteria screen, with message saying "none found"
else if search criteria are matched by exactly one record
display edit screen with chosen record
else (we have lots of records)
display list screen with matching records
Where should that logic go? Not in the view or model surely? Hence this is the job of the controller. The controller would also be responsible for taking the criteria and invoking the Model method for the search.
<?php
class App {
protected static $router;
public static function getRouter() {
return self::$router;
}
public static function run($uri) {
self::$router = new Router($uri);
//get controller class
$controller_class = ucfirst(self::$router->getController()) . 'Controller';
//get method
$controller_method = strtolower((self::$router->getMethodPrefix() != "" ? self::$router->getMethodPrefix() . '_' : '') . self::$router->getAction());
if(method_exists($controller_class, $controller_method)){
$controller_obj = new $controller_class();
$view_path = $controller_obj->$controller_method();
$view_obj = new View($controller_obj->getData(), $view_path);
$content = $view_obj->render();
}else{
throw new Exception("Called method does not exists!");
}
//layout
$route_path = self::getRouter()->getRoute();
$layout = ROOT . '/views/layout/' . $route_path . '.phtml';
$layout_view_obj = new View(compact('content'), $layout);
echo $layout_view_obj->render();
}
public static function redirect($uri){
print("<script>window.location.href='{$uri}'</script>");
exit();
}
}
<?php
class Router {
protected $uri;
protected $controller;
protected $action;
protected $params;
protected $route;
protected $method_prefix;
/**
*
* #return mixed
*/
function getUri() {
return $this->uri;
}
/**
*
* #return mixed
*/
function getController() {
return $this->controller;
}
/**
*
* #return mixed
*/
function getAction() {
return $this->action;
}
/**
*
* #return mixed
*/
function getParams() {
return $this->params;
}
function getRoute() {
return $this->route;
}
function getMethodPrefix() {
return $this->method_prefix;
}
public function __construct($uri) {
$this->uri = urldecode(trim($uri, "/"));
//defaults
$routes = Config::get("routes");
$this->route = Config::get("default_route");
$this->controller = Config::get("default_controller");
$this->action = Config::get("default_action");
$this->method_prefix= isset($routes[$this->route]) ? $routes[$this->route] : '';
//get uri params
$uri_parts = explode("?", $this->uri);
$path = $uri_parts[0];
$path_parts = explode("/", $path);
if(count($path_parts)){
//get route
if(in_array(strtolower(current($path_parts)), array_keys($routes))){
$this->route = strtolower(current($path_parts));
$this->method_prefix = isset($routes[$this->route]) ? $routes[$this->route] : '';
array_shift($path_parts);
}
//get controller
if(current($path_parts)){
$this->controller = strtolower(current($path_parts));
array_shift($path_parts);
}
//get action
if(current($path_parts)){
$this->action = strtolower(current($path_parts));
array_shift($path_parts);
}
//reset is for parameters
//$this->params = $path_parts;
//processing params from url to array
$aParams = array();
if(current($path_parts)){
for($i=0; $i<count($path_parts); $i++){
$aParams[$path_parts[$i]] = isset($path_parts[$i+1]) ? $path_parts[$i+1] : null;
$i++;
}
}
$this->params = (object)$aParams;
}
}
}
Create folder structure
Setup .htaccess & virtual hosts
Create config class to build config array
Controller
Create router class with protected non static, with getters
Create init.php with config include & autoload and include paths (lib, controlelrs,models)
Create config file with routes, default values (route, controllers, action)
Set values in router - defaults
Set uri paths, explode the uri and set route, controller, action, params ,process params.
Create app class to run the application by passing uri - (protected router obj, run func)
Create controller parent class to inherit all other controllers (protected data, model, params - non static)
set data, params in constructor.
Create controller and extend with above parent class and add default method.
Call the controller class and method in run function. method has to be with prefix.
Call the method if exisist
Views
Create a parent view class to generate views. (data, path) with default path, set controller, , render funcs to
return the full tempalte path (non static)
Create render function with ob_start(), ob_get_clean to return and send the content to browser.
Change app class to parse the data to view class. if path is returned, pass to view class too.
Layouts..layout is depend on router. re parse the layout html to view and render
Please check this:
<?php
global $conn;
require_once("../config/database.php");
require_once("../config/model.php");
$conn= new Db;
$event = isset($_GET['event']) ? $_GET['event'] : '';
if ($event == 'save') {
if($conn->insert("employee", $_POST)){
$data = array(
'success' => true,
'message' => 'Saving Successful!',
);
}
echo json_encode($data);
}
if ($event == 'update') {
if($conn->update("employee", $_POST, "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Update Successful!',
);
}
echo json_encode($data);
}
if ($event == 'delete') {
if($conn->delete("employee", "id=" . $_POST['id'])){
$data = array(
'success' => true,
'message' => 'Delete Successful!',
);
}
echo json_encode($data);
}
if ($event == 'edit') {
$data = $conn->get("select * from employee where id={$_POST['id']};")[0];
echo json_encode($data);
}
?>