Load configuration file just once - php

I'm working on my script to convert legacy links to seo friendly urls.
index.php
require 'AltoRouter.php';
$router = new AltoRouter();
$router->setBasePath('/router');
$urls = [
'index.php?option=com_index&task=articles&id=1',
'index.php?option=com_index&task=articles&slug=1-article-title',
'index.php?option=com_index&task=articles.category&cid=100-category1',
'index.php?option=com_shop&task=products&slug=100-amazing-product',
];
foreach($urls as $i=>$url) {
echo $router->getSefUrl($url);
}
AltoRouter.php
...
public function getSefUrl($url) {
$url_clean = str_replace('index.php?', '', $url);
parse_str($url_clean, $output);
$component = empty($output['option']) ? 'com_index' : $output['option'];
$task = empty($output['task']) ? 'index' : $output['task'];
$path = 'components/'.$component.'/routes/routes.json';
$data = json_decode(file_get_contents($path));
if (!empty($data)) {
foreach($data as $route) {
$this->map($route[0], $route[1], $route[2], $route[2]);
}
}
$route_info = $this->findUrlFromRoutes($task);
return empty($route_info) ? $url : $this->generate($route_info->task, $output);
}
...
My question: Every time when I'm using getSefUrl method I'm loading routes from external file. Is it ok? Or can I optimize code above some kind? If yes - how to?
Thanks!

You could avoid multiple fetches and decodes in your loop by breaking that out.
In AltoRouter.php
private $routes = array();
function getComponentRoutes($component)
{
if(! isset($this->routes[$component])) {
$path = 'components/'.$component.'/routes/routes.json';
$this->routes[$component] = json_decode(file_get_contents($path));
}
return $this->routes[$component];
}

You can replace that require with require_once or better use autoloading :
You may define an __autoload() function which is automatically called
in case you are trying to use a class/interface which hasn't been
defined yet. By calling this function the scripting engine is given a
last chance to load the class before PHP fails with an error.
Create a folder and put all your required classs in this folder:
function __autoload($class) {
require_once "Classes" . $class . '.php';
}

Related

How does a php program decide what function is called first when I go to a php page?

I am trying to understand how a php application that is called with a POST to this URL works:
transliterator/romaji
The romaji.php looks like this:
<?php
namespace JpnForPhp\Transliterator;
class Romaji extends TransliterationSystem
{
private $latinCharacters = array();
public function __construct($system = '')
{
$file = __DIR__ . DIRECTORY_SEPARATOR . 'Romaji' . DIRECTORY_SEPARATOR . (($system) ? $system : 'hepburn') . '.yaml';
parent::__construct($file);
}
public function __toString()
{
return $this->configuration['name']['english'] . ' (' . $this->configuration['name']['japanese'] . ')';
}
TransliterationSystem looks like this:
<?php
namespace JpnForPhp\Transliterator;
use Symfony\Component\Yaml\Yaml;
abstract class TransliterationSystem
{
public $configuration = array();
public function __construct($file)
{
$this->configuration = Yaml::parse(file_get_contents($file));
}
public function transliterate($str)
{
$str = $this->preTransliterate($str);
foreach ($this->configuration['workflow'] as $work) {
if (!method_exists($this, $work['function'])) {
continue;
}
$params = array($str);
if (isset($work['parameters'])) {
$params[] = $work['parameters'];
}
$str = call_user_func_array(array($this, $work['function']), $params);
}
$str = $this->postTransliterate($str);
return $str;
}
Can someone explain to me the sequence of events for when I POST to romaji.php? Below is a link to the github if there is something that I should have included but didn't.
For reference here's the link to github
Normally a PHP file is read (and evaluated) from top to bottom. As pointed out in a comment above, these are just class declarations - there's no code there to actually instantiate the classes or do anything with them, so there's really nothing happening here as such.
For something to happen, there would need to be some code to make use of these classes, for example:
$r = new Romaji();
// Do something with $r ....
EDIT:
I just had a look at the GitHub link, and apparently this is a library; so you'll call it from your own code - it won't do anything by itself.
I'm the one who wrote this library :)
JpnForPhp exposes various helper and functions, that's why you don't see any instanciation like = new Romaji() ; the transliterator component doesn't call himself :)
If you want to see some sample to understand how to use it, please check the test files or the demo website source code
Hope this help.

Slim Route Passing to another file Returning Value

I have a folder structure like this :
|/app
|-/db/users.php
|/Slim
|/vendor
|index.php
and inside index.php:
require 'vendor/autoload.php';
$app = new Slim\App();
$app->get('/{action}/{type}/{props}', function ($request, $response, $args) {
$jenis_user = strtolower($args['type']);
$aksi = strtolower($args['action']);
$prop = strtolower($args['props']);
$target_loc = "";
if($aksi == 'get'){
// the parameter is using the following format
// action=XX&type=YY&props=ZZ;
$target_loc = "./app/db/users.php?action=". $aksi . "&type=" . $jenis_user . "&props=" . $prop ;
// #Tips :: data extracted are in Array Format ::
$file = file_get_contents($target_loc);
$response->write(json_encode($file));
}
return $response;
});
My purpose is to get an integer value returned if I use the following path into the browser:
http://api.myweb.com/get/seller/totalNumber
But it seems the way I wrote the routing of passing parameter is mistaken: I got a warning of
Warning: file_get_contents(./app/db/users.php?action=get&type=seller&props=totalnumber): failed to open stream
How could I fix it?
You are trying to get a file with the name users.php?action=get&type=seller&props=totalnumber which doesn't exist, in the file:// protocol, when you really what to do this you need to extra specify the http or https protocol like this:
$content = file_get_contents('http://example.com/users.php?x=y');
and it will succeed.
Notice: this is a bad structur, it would be better when you define a function or class for this, it could be like this:
functions.php
function getUsers($type, $props) {
// do your stuff
return $yourUsers;
}
index.php or route file
include 'functions.php'; // include the functions
$app = new Slim\App();
$app->get('/{action}/{type}/{props}', function ($request, $response, $args) {
$jenis_user = strtolower($args['type']);
$aksi = strtolower($args['action']);
$prop = strtolower($args['props']);
if($aksi == 'get'){
$users = getUsers($jenis_user, $prop); // execute the function to collect the user
$response->write(json_encode($users ));
}
return $response;
});
Then you wouldn't need the file_get_contents().

Instantiating all classes in directory

I'm using Laravel and creating artisan commands but I need to register each one in start/artisan.php by calling
Artisan::add(new MyCommand);
How can I take all files in a directory (app/commands/*), and instantiate every one of them in an array ? I'd like to get something like (pseudocode) :
$my_commands = [new Command1, new Command2, new Command3];
foreach($my_commands as $command){
Artisan::add($command);
}
Here is a way to auto-register artisan commands. (This code was adapted from the Symfony Bundle auto-loader.)
function registerArtisanCommands($namespace = '', $path = 'app/commands')
{
$finder = new \Symfony\Component\Finder\Finder();
$finder->files()->name('*Command.php')->in(base_path().'/'.$path);
foreach ($finder as $file) {
$ns = $namespace;
if ($relativePath = $file->getRelativePath()) {
$ns .= '\\'.strtr($relativePath, '/', '\\');
}
$class = $ns.'\\'.$file->getBasename('.php');
$r = new \ReflectionClass($class);
if ($r->isSubclassOf('Illuminate\\Console\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) {
\Artisan::add($r->newInstance());
}
}
}
registerArtisanCommands();
If you put that in your start/artisan.php file, all commands found in app/commands will be automatically registered (assuming you follow Laravel's recommendations for command and file names). If you namespace your commands like I do, you can call the function like so:
registerArtisanCommands('App\\Commands');
(This does add a global function, and a better way to do this would probably be creating a package. But this works.)
<?php
$contents = scandir('dir_path');
$files = array();
foreach($contents as $content) {
if(substr($content,0,1 == '.') {
continue;
}
$files[] = 'dir_path'.$content;
}
That reads the contents of a folder, itterates over it and saves the filename including path in the $files array. Hope this is what you're looking for
PS: Im not familiar with laravel or artisan. So if you have to use specific semantics(like camelCase) to register them, then please tell me so

Get correct url / page to load in simple MVC

I´m making a "very" simple MVC framework in order to learn, however I have trouble getting other pages than the index page to show. In views folder I have 2 files one index.php and one register.php that I´m trying on.
I have tried various ways but can´t get my head around it. I know it is probably best to put different controller classes in different files and maybe a loader controller page but I´m a beginner with php so would like to make it as simple as possible for me...
Any help appriciated!
I have a index.php as a landing file in the root folder to bind everything together:
<?php
/* index.php
*
*/
require_once 'model/load.php';
require_once 'controller/main.php';
new mainController();
In the controller folder i have a file called main.php:
<?php
/* controller/main.php
*
*/
class mainController
{
public $load;
public function __construct()
{
$urlValues = $_SERVER['REQUEST_URI'];
$this->urlValues = $_GET;
//index page
if ($this->urlValues['controller'] == "") {
$indexPage = array("key" => "Hello");
$this->load = new load();
$this->load->view('index.php', $indexPage);
}
//register page
if ($this->urlValues['controller'] == "register.php") {
$registerPage = array("key" => "Register");
$this->load = new load();
$this->load->view('register.php', $registerPage);
}
}
}
And then I have a file called load.php in the model folder:
<?php
/* model/load.php
*
*/
class load
{
/* This function takes parameter
* $file_name and match with file in views.
*/
function view($file_name, $data = null)
{
if (is_readable('views/' . $file_name)) {
if (is_array($data)) {
extract($data);
}
require 'views/' . $file_name;
} else {
echo $this->file;
die ('404 Not Found');
}
}
}
In your mainController class you don't have property with the name urlValues, but you use it: $this->urlValues = $_GET;. And what is more you have local variable with the same name, that you don't use: $urlValues = $_SERVER['REQUEST_URI'];
And how you URL for register.php looks like?

Simple PHP Routing Project

I need to create a simple routing mechanism that takes a request like: /foo/bar and translates it to FooController->barAction(); I have to use a single script as an access point to load these controller classes and action methods. I also cannot use any external frameworks or libraries to accomplish this task. This needs to be able to be run on a PHP 5.3 Server with Apache.
Below is what I've written already, but I'm having trouble getting it to work:
class Router {
private static $routes = array();
private function __construct() {}
private function __clone() {}
public static function route($pattern, $callback) {
$pattern = '/' . str_replace('/', '\/', $pattern) . '/';
self::$routes[$pattern] = $callback;
}
public static function execute() {
$url = $_SERVER['REQUEST_URI'];
$base = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));
if (strpos($url, $base) === 0) {
$url = substr($url, strlen($base));
}
foreach (self::$routes as $pattern => $callback) {
if (preg_match($pattern, $url, $params)) {
array_shift($params);
return call_user_func_array($callback, array_values($params));
}
}
}
}
I'm trying to at least execute my current script which is based off another simple Router, but I cannot actually get an output using
Router::route('blog/(\w+)/(\d+)', function($category, $id){
print $category . ':' . $id;
});
Router::execute();
Instead of trying to break out the PATH. Why not use .htaccess instead.
So you could have internal URL's that look like this:
index.php?module=MODULE&action=INDEX
Then use .htaccess to provide the paths in the URL and the route them accordingly.
www.mydomain.com/MODULE/INDEX
This post can help with the rewriting regex for creating pritty urls in htaccess
There might be a better one, was just a quick google search.
This way you can access like this:
$module = $_GET['module'];
$action = $_GET['action];
Then you can do checks to corresponding actions in your router to check if it exists and then re-route accordingly.

Categories