Im using a code to separate pages that is HTTPS and HTTP in my website
The problem is: When Im on HTTP, links to HTTPS no have WWW and vice versa.
I did not find the problem in the script.
public function createUrl($route, $params = array(), $ampersand = '&')
{
$url = parent::createUrl($route, $params, $ampersand);
// If already an absolute URL, return it directly
if (strpos($url, 'http') === 0) {
return $url;
}
// Check if the current protocol matches the expected protocol of the route
// If not, prefix the generated URL with the correct host info.
$secureRoute = $this->isSecureRoute($route);
if (Yii::app()->request->isSecureConnection) {
return $secureRoute ? $url : 'http://' . Yii::app()->request->serverName . $url;
} else {
return $secureRoute ? 'https://' . Yii::app()->request->serverName . $url : $url;
}
}
public function parseUrl($request)
{
$route = parent::parseUrl($request);
// Perform a 301 redirection if the current protocol
// does not match the expected protocol
$secureRoute = $this->isSecureRoute($route);
$sslRequest = $request->isSecureConnection;
if ($secureRoute !== $sslRequest) {
$hostInfo = $secureRoute ? 'https://' . Yii::app()->request->serverName : 'http://' . Yii::app()->request->serverName;
if ((strpos($hostInfo, 'https') === 0) xor $sslRequest) {
$request->redirect($hostInfo . $request->url, true, 301);
}
}
return $route;
}
private $_secureMap;
/**
* #param string the URL route to be checked
* #return boolean if the give route should be serviced in SSL mode
*/
protected function isSecureRoute($route)
{
if ($this->_secureMap === null) {
foreach ($this->secureRoutes as $r) {
$this->_secureMap[strtolower($r)] = true;
}
}
$route = strtolower($route);
if (isset($this->_secureMap[$route])) {
return true;
} else {
return ($pos = strpos($route, '/')) !== false
&& isset($this->_secureMap[substr($route, 0, $pos)]);
}
}
}
Code adapted from: http://www.yiiframework.com/wiki/407/url-management-for-websites-with-secure-and-nonsecure-pages/
It's better to manage this at the controller level using filters.
In your components directory setup 2 filters HttpsFilter and HttpFilter as follows:-
class HttpsFilter extends CFilter {
protected function preFilter( $filterChain ) {
if ( !Yii::app()->getRequest()->isSecureConnection ) {
# Redirect to the secure version of the page.
$url = 'https://' .
Yii::app()->getRequest()->serverName .
Yii::app()->getRequest()->requestUri;
Yii::app()->request->redirect($url);
return false;
}
return true;
}
}
and
class HttpFilter extends CFilter {
protected function preFilter( $filterChain ) {
if ( Yii::app()->getRequest()->isSecureConnection ) {
# Redirect to the secure version of the page.
$url = 'http://' .
Yii::app()->getRequest()->serverName .
Yii::app()->getRequest()->requestUri;
Yii::app()->request->redirect($url);
return false;
}
return true;
}
}
then in each controller force https using the filters, optionally by action:
class SiteController extends Controller {
public function filters()
{
return array(
'https +index', // Force https, but only on login page
);
}
}
Edit: if the filters() function above doesn't seem to work for you, instead try
return array(
array('HttpsFilter +index'), // Force https, but only on login page
);
See http://www.yiiframework.com/doc/guide/1.1/en/basics.controller#filter (and comments on it).
Related
I have the following methods:
public function isAdmin()
{
$adminUrl = $this->getPage(2)->httpUrl;
/*
* Remove all segments except the first in the URL path
* to detect if current page is in admin view
*/
$currentUrl = preg_replace( // https://regex101.com/r/D0mOz9/1
sprintf(
'~(?<adminUrl>%s)(?<remove>.*)$~',
addslashes($adminUrl)
),
'$1',
$this->getPage()->httpUrl
);
return $adminUrl == $currentUrl || (!$currentUrl && !$this->getPage() instanceof NullPage);
}
public function getPage($page = null)
{
$wiredPage = $this->wire('page');
if (!is_null($wiredPage) && is_null($page)) {
$baseUrl = $this->wire('page')->url;
$urlSegmentStr = $this->wire('input')->urlSegmentStr;
if (strlen($urlSegmentStr)) {
$baseUrl = rtrim($baseUrl, '/') . "/$urlSegmentStr/";
}
return $this->pages->get($baseUrl);
}
if (is_null($page)) {
return $this->pages->get(parse_url(getenv('REQUEST_URI'), PHP_URL_PATH));
}
return $this->pages->get($page);
}
I check if for an admin page by getting the url to the admin page and compare it to the current url.
Now I want to write a test for it with PHPunit but I have no idea how to test a method which result is based on urls like this.
Is it possible to write a test for a case like this? How would I be able to do this?
I'm making a toolkit for php applications. I've a made a routing system based on some conventions, it works well but i would like to learn how to make mod_rewrite rules or any other stuff to finally make the url good to see and good for seo.
The route system starts from a config file that set the app and url roots.
$app_root = $_SERVER["DOCUMENT_ROOT"].dirname($_SERVER["PHP_SELF"])."/";
$app_url = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/';
define("APP_URL",$app_url);
define("APP_ROOT",$app_root);
The route always get start from index.php, wich makes instances of controllers#actions from GET parameters controllers=?&action=?
This is the index.php
<?php
include_once 'controller/Frontend.php';
require 'libraries/Router.php';
$params=array();
if(isset($_GET['controller'])&&isset($_GET['action'])){
$c = $_GET['controller'];
$a = $_GET['action'];
// add all query string additional params to method signature i.e. &id=x&category=y
$queryParams = array_keys($_GET);
$queryValues = array_values($_GET);
for ($i=2;$i<count($queryParams);$i++) {
$params[$queryParams[$i]] = $queryValues[$i];
}
if ($_POST) {
// add all query string additional params to method signature i.e. &id=x&category=y
$queryParams = array_keys($_POST);
$queryValues = array_values($_POST);
for ($i=0;$i<count($_POST);$i++) {
$params[$queryParams[$i]] = $queryValues[$i];
}
}
include_once APP_ROOT."/controller/$c.php";
$controller = new $c();
$controller->$a($params);
} else {
//attiva controller predefinito
$controller = new Frontend();
$controller->index();
}
This allow to select what controller and what action the router must call.
The router function here get the APP URL from settings.php in the root. You give im the two controllers#action params as string and it make the URL like so:
index.php?controller=X&action=Y&[params...]
<?php
require './settings.php';
function router($controller,$action,$query_data="") {
$param = is_array($query_data) ? http_build_query($query_data) : "$query_data";
$url = APP_URL."index.php?controller=$controller&action=$action&$param";
return $url;
}
function relativeRouter ($controller,$action,$query_data=""){
$param = is_array($query_data) ? http_build_query($query_data) : "$query_data";
$url = "index.php?controller=$controller&action=$action&$param";
return $url;
}
function redirectToOriginalUrl() {
$url = $_SERVER['HTTP_REQUEST_URI'];
header("location: $url");
}
function switchAction ($controller, $action) {
$r = router($controller, $action);
header("location:$r", true, 302);
}
In templates file i call router('controller,'action') to retrive url's to actions and also pass GET/POST data (collected from index.php that put's them into the method signature as array).
Don't blame me for using global POST/GET without filtering i'm still developing the thing, security things will be made after ;)
What i would like to ask if someone could share some thoughts on how to make pretty urls like site/page/action....
For example www.site.com/blog/post?id=1
(Actually the N params in the router function ($query_data) works this way, you pass array['id' => '1'] and you get ?id=1)
What are best strategies to make good urls?
Thank you so much, still learning PHP.
If there are best way to do such things just give your feedback.
I found myself an answer to the question, i post here maybe it's useful.
I've added a .htaccess file in the root:
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
This will return each request to the root/index.php file.
Index file collect routes from the HTTP request, check if the route exist in the "routes.json" file.
URL are written in this way:
site.com/controller/action. GET params are written as follows
site.com/controller/action/[params]/[value]...... This output for example site.com/blog/post/id/1
That should be also fine for REST.
Here the index.php
<?php
require 'controller/Frontend.php';
require 'Class/Router.php';
//require 'libraries/Router.php';
/*
* ** ROUTING SETTINGS **
*/
$app_root = $_SERVER["DOCUMENT_ROOT"].dirname($_SERVER["PHP_SELF"])."/";
$app_url = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/';
define("APP_URL",$app_url);
define("APP_ROOT",$app_root);
$basepath = implode('/', array_slice(explode('/', $_SERVER['SCRIPT_NAME']), 0, -1));
$uri = substr($_SERVER['REQUEST_URI'], strlen($basepath));
//echo $uri;
if ($uri == "/") {
$frontend = new Frontend();
$frontend->index();
} else {
$root = ltrim ($uri, '/');
//$paths = explode("/", $uri);
$paths = parse_url($root, PHP_URL_PATH);
$route = explode("/",$paths);
$request = new \PlayPhp\Classes\Request();
// controller
$c = $route[0];
// action
$a = $route[1];
$reverse = Router::inverseRoute($c,$a);
$rest = $_SERVER['REQUEST_METHOD'];
switch ($rest) {
case 'PUT':
//rest_put($request);
break;
case 'POST':
if (Router::checkRoutes($reverse, "POST")) {
foreach ($_POST as $name => $value) {
$request->setPost($name,$value);
}
break;
} else {
Router::notFound($reverse,"POST");
}
case 'GET':
if (Router::checkRoutes($reverse, "GET")) {
for ($i = 2; $i < count($route); $i++) {
$request->setGet($route[$i], $route[++$i]);
}
break;
} else {
Router::notFound($reverse,"GET");
}
break;
case 'HEAD':
//rest_head($request);
break;
case 'DELETE':
//rest_delete($request);
break;
case 'OPTIONS':
//rest_options($request);
break;
default:
//rest_error($request);
break;
}
include_once APP_ROOT.'controller/'.$c.'.php';
$controller = new $c();
$controller->$a($request);
}
The Router class:
<?php
include 'config/app.php';
/*
* Copyright (C) 2015 yuri.blanc
*/
require 'Class/http/Request.php';
class Router {
protected static $routes;
private function __construct() {
Router::$routes = json_decode(file_get_contents(APP_ROOT.'config/routes.json'));
}
public static function getInstance(){
if (Router::$routes==null) {
new Router();
}
return Router::$routes;
}
public static function go($action,$params=null) {
$actions = explode("#", $action);
$c = strtolower($actions[0]);
$a = strtolower($actions[1]);
// set query sting to null
$queryString = null;
if(isset($params)) {
foreach ($params as $name => $value) {
$queryString .= '/'.$name.'//'.$value;
}
return APP_URL."$c/$a$queryString";
}
return APP_URL."$c/$a";
}
public static function checkRoutes($action,$method){
foreach (Router::getInstance()->routes as $valid) {
/* echo $valid->action . ' == ' . $action . '|||';
echo $valid->method . ' == ' . $method . '|||';*/
if ($valid->method == $method && $valid->action == $action) {
return true;
}
}
}
public static function inverseRoute($controller,$action) {
return ucfirst($controller)."#".$action;
}
public static function notFound($action,$method) {
die("Route not found:: $action with method $method");
}
}
I use the json_decode function to parse the json object in stdClass().
The json file looks like this:
{"routes":[
{"action":"Frontend#index", "method":"GET"},
{"action":"Frontend#register", "method":"GET"},
{"action":"Frontend#blog", "method":"GET"}
]}
This way i can whitelist routes with their methods and return 404 errors while not found.
System is still quite basic but gives and idea and works, hope someone will find useful.
we need to have a new function such as base_url() , named main_site_url() to being able to use it exactly as the same as site_url().
I've just added this to main config file in application/config:
$config['main_site_url'] = 'http//iksna.com/';
and this code to /system/core/config.php
/**
* Main Site URL
* Returns main_site_url . index_page [. uri_string]
*
* #access public
* #param string the URI string
* #return string
*/
function main_site_url($uri = '')
{
if ($uri == '')
{
return $this->slash_item('main_site_url').$this->item('index_page');
}
if ($this->item('enable_query_strings') == FALSE)
{
$suffix = ($this->item('url_suffix') == FALSE) ? '' : $this->item('url_suffix');
return $this->slash_item('main_site_url').$this->slash_item('index_page').$this->_uri_string($uri).$suffix;
}
else
{
return $this->slash_item('main_site_url').$this->item('index_page').'?'.$this->_uri_string($uri);
}
}
// -------------------------------------------------------------
but now, it is not accessible by: main_site_url();
is it accessible by: $this->config->main_site_url();
i have this error when is use main_site_url();
error:
Call to undefined function main_site_url()
You can create your own by the following way:
Step 1:
In you application/config/config.php add this, $config['main_site_url'] = 'http//iksna.com/';
Step 2:
In system/core/Config.php add a new function main_site_url() like base_url(), site_url() which are already defined there:
public function main_site_url($uri = '', $protocol = NULL)
{
$main_site_url = $this->slash_item('main_site_url');
if (isset($protocol))
{
if ($protocol === '')
{
$main_site_url = substr($main_site_url, strpos($main_site_url, '//'));
}
else
{
$main_site_url = $protocol.substr($main_site_url, strpos($main_site_url, '://'));
}
}
return $main_site_url.ltrim($this->_uri_string($uri), '/');
}
Step 3:
Now add the following code in system/helpers/url_helper.php
if ( ! function_exists('main_site_url'))
{
function main_site_url($uri = '', $protocol = NULL)
{
return get_instance()->config->main_site_url($uri, $protocol);
}
}
Now you can use main_site_url() anywhere in your controllers, libraries and views just like base_url(), site_url() etc.
Go to
/system/application/libraries
Create one file named
custom_function.php
add function main_site_url inside custom_function.php
call function in controller file using
$this->custom_function->main_site_url();
If $id does not match to $sessionPackage['id'] it should execute
return Redirect::to('/order/' . $id . '/details');
For some reason it does redirect to a page, however it does output "Test" as debugging, so it mean that if condition is working.
In the controller:
public function showDetails($id)
{
if (!$this->checkPackage($id)) {
return Redirect::to('/');
}
$data = SessionOrder::getPersonalDetails();
return View::make("order.order-details")->withData($data);
}
checkPackage() function:
protected function checkPackage($id)
{
$this->package = Packages::getPackage($id);
if (!$this->package) {
return false;
}
$sessionPackage = SessionOrder::getPackage();
if (SessionOrder::hasPackage() && $sessionPackage['id'] != $id) {
SessionOrder::clearPackageExceptPersonalDetails();
SessionOrder::storePackage([
'id' => $id
]);
echo "Test";
return Redirect::to('/order/' . $id . '/details');
}
return true;
}
Update: If package ID does not exist then it would redirect return Redirect::to('/'); but if package ID does exist and it does not match to $sessionPackage['id'] it should redirect to Redirect::to('/order/' . $id . '/details');
Instead of the redirection, you need to return false, and do the redirection in an else-clausule in your showDetails method.
Laravel route doesn't support cascade routing. So you have to redirect in your showDetail funciton, not in the child funciton checkPackage().
I'm trying to create multilingual application. I've implemented ability of translationable content and next step should be showing it to user. I want to have ability of changing language depending on URL. I've found a couple of components for those purposes but they all create urls which I don't like. For example, my application's default language is English and I have content which is translated into French. I have page "contacts", for instance. And URLs which will be generated by application will be: mysite.com/en/contacts, mysite.com/fr/contacts, but I want to have mysite.com/contacts for default language and mysite.com/fr/contacts for French language. It's simillar for site's root too. mysite.com/ - for default language and mysite.com/fr for French.
Is there any methods for implementing these functionality?
I'm using XUrlManager extension XUrlManager on GitHub
Yii generates URL's based on UrlManager rules. If you want URL's without /lang/ code - you need just create correct rules. For example, if you dublicate records in rules array:
'rules'=>array(
'<_c:\w+>/<_a:\w+>'=>'<_c>/<_a>',
'<language:\w{2}>/<_c:\w+>/<_a:\w+>'=>'<_c>/<_a>',
);
your URL's will be generated withou /en/ and /fr/, but URL's with code works too. By default, XUrlManager use previously selected language and store this in session or cookie.
If you want only hide /en/ and use /fr/ and others always, you can change your XUrlManager extension with:
public function createUrl($route,$params=array(),$ampersand='&')
{
if(!isset($params['language']) && Yii::app()->language!=='en')
$params['language']=Yii::app()->language;
return parent::createUrl($route,$params,$ampersand);
}
I've found very elegant method for solving my problem on http://www.elisdn.ru
Reimplement CHttpRequest
class DLanguageHttpRequest extends CHttpRequest
{
private $_requestUri;
public function getRequestUri()
{
if ($this->_requestUri === null)
$this->_requestUri = DMultilangHelper::processLangInUrl(parent::getRequestUri());
return $this->_requestUri;
}
public function getOriginalUrl()
{
return $this->getOriginalRequestUri();
}
public function getOriginalRequestUri()
{
return DMultilangHelper::addLangToUrl($this->getRequestUri());
}
}
Reimplement CUrlManager
class DLanguageUrlManager extends CUrlManager
{
public function createUrl($route, $params=array(), $ampersand='&')
{
$url = parent::createUrl($route, $params, $ampersand);
return DMultilangHelper::addLangToUrl($url);
}
}
Change config
return array(
'sourceLanguage'=>'en',
'language'=>'ru',
'components'=>array(
'request'=>array(
'class'=>'DLanguageHttpRequest',
...
),
'urlManager'=>array(
'class'=>'DLanguageUrlManager',
...
),
),
...
'params'=>array(
'translatedLanguages'=>array(
'ru'=>'Russian',
'en'=>'English',
'de'=>'Deutsch',
),
'defaultLanguage'=>'ru',
),
);
Create DMultilangHelper
class DMultilangHelper
{
public static function enabled()
{
return count(Yii::app()->params['translatedLanguages']) > 1;
}
public static function suffixList()
{
$list = array();
$enabled = self::enabled();
foreach (Yii::app()->params['translatedLanguages'] as $lang => $name)
{
if ($lang === Yii::app()->params['defaultLanguage']) {
$suffix = '';
$list[$suffix] = $enabled ? $name : '';
} else {
$suffix = '_' . $lang;
$list[$suffix] = $name;
}
}
return $list;
}
public static function processLangInUrl($url)
{
if (self::enabled())
{
$domains = explode('/', ltrim($url, '/'));
$isLangExists = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages']));
$isDefaultLang = $domains[0] == Yii::app()->params['defaultLanguage'];
if ($isLangExists && !$isDefaultLang)
{
$lang = array_shift($domains);
Yii::app()->setLanguage($lang);
}
$url = '/' . implode('/', $domains);
}
return $url;
}
public static function addLangToUrl($url)
if (self::enabled())
{
$domains = explode('/', ltrim($url, '/'));
$isHasLang = in_array($domains[0], array_keys(Yii::app()->params['translatedLanguages']));
$isDefaultLang = Yii::app()->getLanguage() == Yii::app()->params['defaultLanguage'];
if ($isHasLang && $isDefaultLang)
array_shift($domains);
if (!$isHasLang && !$isDefaultLang)
array_unshift($domains, Yii::app()->getLanguage());
$url = '/' . implode('/', $domains);
}
return $url;
}
}
After all of these steps your application will have URLs which you want
More information here