Jak simulovat HttpRequest?
- Tomik
- Nette Evangelist | 485
Zdravím!
Rád bych do svého systému implementoval tuto vlastnost – při přidání,
resp. upravení textu se veškerá URI, která jdou na některý z Presenterů
aplikace nahradí za nějakou pseudo-syntax (klidně podobnou
CurlyBrackets
), např. URI http://www.web.cz/ahoj/edit
se nahradí za {link Pages:edit 'uri' => 'ahoj'}
, či něco
podobného, poté při zobrazování tuto pseudo-syntax pouze převedu na
aktuální URI. Jakou to má výhodu? Může se změnit schéma URI webu
(myšleno definice rout) a není třeba v žádném textu upravovat
jediný odkaz.
A teď k dotazu:
Už jsem se pustil do implementace, regulárem najdu všechna URI v odkazech
(resp. v src|href|action|on[a-z]+
), a potom bych na tyto
jednotlivá URI chtěl aplikovat podobný princip zpracování jako probíhá
u RoutingDebuggeru. A pokud dojde k schodě, tj. nalezení
Presenteru a action k danému URI, pak jej nahradit za pseudo-syntax odkazu,
pokud ke shodě nedojde, pak nechat normální URI – to které tam bylo (tj.
URI pravděpodobně vede mimo web, resp. vede např. na obrázek, či styl) –
proto jej nebudu nahrazovat.
Ale! Princip zjišťování
v RoutingDebuggeru zda URI odpovídá nějaké routě (resp.
jejímu presenteru
a action
) je založen na
HttpRequest
, a tu nelze vytvořit jinou než s URI právě
položeného požadavku. Chci se tedy zeptat, zda je možné nějak
HttpRequest
objekt nějak simulovat pro konkrétní URI (tj. jinou
adresu, než která je pro právě položený request – tedy pro jinou
adresu, než která je zobrazena v adres-baru), nebo je nutné kompletně pro
tento požadavek přepsat metodu match
v Routě
(tak,
aby pokud to potřebuji nebrala informace z HttpRequest
, ale třeba
z objektu Uri
, který jí nějak předám).
Díky za rady. :)
- Jan Tvrdík
- Nette guru | 2595
Nešlo by podědit a upravit HttpRequest
nebo vyrobit vlastní
třídu implementující IHttpRequest
?
PS: Nechápu, co myslíš tím simulováním RoutingDebuggeru. Mělo by
stačit zavolat
Environment::getApplication()->router->match($httpRequest)
.
EDIT: Vypadá to, že stačí, když si třídu HttpRequest
podědíš a napíšeš si metodu __construct
, která naplní
atributy hodnotami, takže k volání detectUri
a
initialize
vůbec nedojde.
Editoval Jan Tvrdík (8. 5. 2009 18:03)
- kravčo
- Člen | 721
Vzhľadom na to, ako je napísaný HttpRequest, cezeň to asi nepôjde.
Vďaka otvorenosti však Router vyžaduje interface
IHttpRequest
, čiže ti nič nebráni spraviť veľmi jednoduchý
MockHttpRequest
, ktorému v konštruktore alebo pomocou setterov
nastavíš všetko čo treba…
class MockHttpRequest extends Object implements IHttpRequest
{
public function __construct($uri)
{
// ...
}
public function setUri($uri)
{
// ...
}
// ...
}
- Tomik
- Nette Evangelist | 485
kravco napsal(a):
Vzhľadom na to, ako je napísaný HttpRequest, cezeň to asi nepôjde. Vďaka otvorenosti však Router vyžaduje interface
IHttpRequest
, čiže ti nič nebráni spraviť veľmi jednoduchýMockHttpRequest
, ktorému v konštruktore alebo pomocou setterov nastavíš všetko čo treba…
O tomhle jsem taky uvažoval. Asi to bude nejjednodušší. Díky.
- Tomik
- Nette Evangelist | 485
Jan Tvrdík napsal(a):
PS: Nechápu, co myslíš tím simulováním RoutingDebuggeru. Mělo by stačit zavolat
Environment::getApplication()->router->match($httpRequest)
.
Máš pravdu, tohle mě fakt nenapadlo. :) Já zbytečně procházel každou subroutu Multirouteru. Nějak mě nedoclaklo, že zjistit Routu, která odpovídá nějakému requestu jde mnohem snáze. ;)
Díky vám oboum za spolupráci. ;)
- xificurk
- Člen | 121
Doufám, že jsem vykopíroval ze svých Helperů vše podstatné…
Použití:
- Před uložením textu do databáze zavolat
Helpers::url2PresenterLink($text)
- Vytažený text z databáze v šabloně můžeme prohnat helperem, příp.
Texy pomocí
{!$text|PL2U|texy}
<?php
/**
* Helpers class
*
* Copyright (c) 2008, 2009 Petr Moravek [Xificurk]
*
* This source file is subject to the General Public License (GPL)
* http://www.gnu.org/copyleft/gpl.txt
*
* This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* @copyright Copyright (c) 2008, 2009 Petr Moravek [Xificurk]
* @license General Public License (GPL), http://www.gnu.org/copyleft/gpl.txt
* @version 0.2
*/
/**
* Helpers class.
*
* @author Petr Morávek [Xificurk]
* @copyright Copyright (c) 2008 Petr Morávek [Xificurk]
*/
class Helpers extends Object
{
/**
* Static class - cannot be instantiated.
*/
final public function __construct()
{
throw new LogicException("Cannot instantiate static class " . get_class($this));
}
/**
* @param string name
* @return callback
*/
public static function helperLoader($name)
{
switch ($name) {
case 'pl2u':
return array('Helpers', 'presenterLink2Url');
}
}
/********************* links *********************/
/** @const */
/* PCRE for URL taken from David Grudl's Texy! class TexyLinkModule (deleted ftp scheme) // GPL license */
const PAT_URL = '#(?<=^|[\s([<:\x17])(?:https?://|www\.)[0-9.A-Za-z\x{C0}-\x{2FF}\x{370}-\x{1EFF}-][/\dA-Za-z\x{C0}-\x{2FF}\x{370}-\x{1EFF}+\.~%&?@=_:;\#,\x{ad}-]+[/\dA-Za-z\x{C0}-\x{2FF}\x{370}-\x{1EFF}+~%?@=_\#]#u';
/**
* Convert URLs to Presenter Links where possible.
* @param string text
* @return string
*/
public static function url2PresenterLink($text)
{
return preg_replace_callback(self::PAT_URL, array(__CLASS__, 'cb_url2PresenterLink'), $text);
}
/**
* Callback for url2PresenterLink
* @param array
* @return string
*/
private static function cb_url2PresenterLink($url)
{
$req = preg_replace('#^www\.#i', 'http://', $url[0]);
$req = new UriHttpRequest($req);
$app = Environment::getApplication()->getRouter()->match($req);
if (!$app) {
return $url[0];
}
$params = $app->params;
$presenterName = ':'. $app->presenterName . ':' . $params['action'];
unset($params['action']);
$arguments = '';
if (!empty($params)) {
$arguments = ', ' . preg_replace('#^array\s*\((.*)\)$#i', '\1', str_replace("\n", '', var_export($params, true)));
}
$plink = '{plink //' .
$presenterName .
(!empty($req->uri->fragment) ? '#' . $req->uri->fragment : '') .
$arguments .
'}';
return $plink;
}
/**
* Converts {plink :Module:Presenter:view array('param1' => 'val')} to URL where possible
* @param string text
* @return string
*/
public static function presenterLink2Url($string)
{
return preg_replace_callback(
'~\\{plink( [^}]*?)\\}~xsi',
array(__CLASS__, 'cb_presenterLink2Url'),
$string
);
}
/**
* Callback for presenterLink2Url
* @param array
* @return string
*/
private static function cb_presenterLink2Url($link)
{
$var = trim($link[1]);
$presenterName = NULL;
$presenterParams = array();
if (preg_match('#^([^\s,]+),?\s*(.*)$#', $var, $m)) {
$presenterName = preg_replace('#[\'"]?(.*)[\'"]#', '\\1', $m[1]);
if ($m[2]) {
eval('$presenterParams = array(' . $m[2] . ');');
}
}
$oldMode = Presenter::$invalidLinkMode;
Presenter::$invalidLinkMode = Presenter::INVALID_LINK_EXCEPTION;
try {
$ret = TemplateHelpers::escapeHtml(Environment::getApplication()->presenter->link($presenterName, $presenterParams));
} catch (Exception $e) {
$ret = $link[0];
}
Presenter::$invalidLinkMode = $oldMode;
return $ret;
}
}
?>
<?php
/**
* UriHttpRequest class
*
* Copyright (c) 2008, 2009 Petr Moravek [Xificurk]
*
* This source file is subject to the General Public License (GPL)
* http://www.gnu.org/copyleft/gpl.txt
*
* This program is distributed in the hope that it will be useful - WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* @copyright Copyright (c) 2008, 2009 Petr Moravek [Xificurk]
* @license General Public License (GPL), http://www.gnu.org/copyleft/gpl.txt
* @version 0.3
*/
/**
* UriHttpRequest class.
*
* @author Petr Morávek [Xificurk]
* @copyright Copyright (c) 2008 Petr Morávek [Xificurk]
*/
class UriHttpRequest extends Object implements IHttpRequest
{
/** @var array */
protected $query;
/** @var UriScript {@link HttpRequest::getUri()} */
protected $uri;
/**
* @param string URI
*/
public function __construct($uri)
{
$origUri = parse_url($uri);
$uri = $this->uri = new UriScript;
$uri->scheme = isset($origUri['scheme']) ? $origUri['scheme'] : 'http';
$uri->path = isset($origUri['path']) ? $origUri['path'] : '/';
$uri->user = isset($origUri['user']) ? $origUri['user'] : '';
$uri->pass = isset($origUri['pass']) ? $origUri['pass'] : '';
$uri->host = isset($origUri['host']) ? $origUri['host'] : '';
$uri->port = isset($origUri['port']) ? $origUri['port'] : 80;
$uri->fragment = isset($origUri['fragment']) ? $origUri['fragment'] : '';
if (!empty($origUri['query'])) {
parse_str($origUri['query'], $this->query);
} else {
$this->query = array();
}
// Taken from David Grudl's Nette class Nette\HttpRequest->detectUri() (rev 283) // Nette license
// normalized uri
$uri->canonicalize();
// detect base URI-path - inspired by Zend Framework (c) Zend Technologies USA Inc. (http://www.zend.com), new BSD license
$filename = basename($_SERVER['SCRIPT_FILENAME']);
if (basename($_SERVER['SCRIPT_NAME']) === $filename) {
$scriptPath = rtrim($_SERVER['SCRIPT_NAME'], '/');
} elseif (basename($_SERVER['PHP_SELF']) === $filename) {
$scriptPath = $_SERVER['PHP_SELF'];
} elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
$scriptPath = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
} else {
// Backtrack up the script_filename to find the portion matching php_self
$path = $_SERVER['PHP_SELF'];
$segs = explode('/', trim($_SERVER['SCRIPT_FILENAME'], '/'));
$segs = array_reverse($segs);
$index = 0;
$last = count($segs);
$scriptPath = '';
do {
$seg = $segs[$index];
$scriptPath = '/' . $seg . $scriptPath;
$index++;
} while (($last > $index) && (FALSE !== ($pos = strpos($path, $scriptPath))) && (0 != $pos));
}
// Does the scriptPath have anything in common with the request_uri?
if (strncmp($uri->path, $scriptPath, strlen($scriptPath)) === 0) {
// whole $scriptPath in URL
$uri->scriptPath = $scriptPath;
} elseif (strncmp($uri->path, $scriptPath, strrpos($scriptPath, '/') + 1) === 0) {
// directory portion of $scriptPath in URL
$uri->scriptPath = substr($scriptPath, 0, strrpos($scriptPath, '/') + 1);
} elseif (strpos($uri->path, basename($scriptPath)) === FALSE) {
// no match whatsoever; set it blank
$uri->scriptPath = '/';
} elseif ((strlen($uri->path) >= strlen($scriptPath))
&& ((FALSE !== ($pos = strpos($uri->path, $scriptPath))) && ($pos !== 0))) {
// If using mod_rewrite or ISAPI_Rewrite strip the script filename
// out of scriptPath. $pos !== 0 makes sure it is not matching a value
// from PATH_INFO or QUERY_STRING
$uri->scriptPath = substr($uri->path, 0, $pos + strlen($scriptPath));
} else {
$uri->scriptPath = $scriptPath;
}
}
/**
* Returns URL object.
* @return UriScript
*/
function getUri()
{
return $this->uri;
}
/**
* Returns variable provided to the script via URL query ($_GET).
* If no key is passed, returns the entire array.
* @param string key
* @param mixed default value
* @return mixed
*/
function getQuery($key = NULL, $default = NULL)
{
if (func_num_args() === 0) {
return $this->query;
} elseif (isset($this->query[$key])) {
return $this->query[$key];
} else {
return $default;
}
}
/**
* Returns variable provided to the script via POST method ($_POST).
* If no key is passed, returns the entire array.
* @param string key
* @param mixed default value
* @return mixed
*/
function getPost($key = NULL, $default = NULL)
{
if (func_num_args() === 0) {
// We don't have post, return empty array
return array();
} else {
return $default;
}
}
/**
* Returns HTTP POST data in raw format (only for "application/x-www-form-urlencoded").
* @return string
*/
function getPostRaw()
{
// We don't have post
return "";
}
/**
* Returns uploaded file.
* @param string key (or more keys)
* @return HttpUploadedFile
*/
function getFile($key)
{
if (func_num_args() === 0) {
// We don't have files, return empty array
return array();
} else {
return NULL;
}
}
/**
* Returns uploaded files.
* @return array
*/
function getFiles()
{
// We don't have files, return empty array
return array();
}
/**
* Returns variable provided to the script via HTTP cookies.
* @param string key
* @param mixed default value
* @return mixed
*/
function getCookie($key, $default = NULL)
{
if (func_num_args() === 0) {
// We don't have cookies, return empty array
return array();
} else {
return $default;
}
}
/**
* Returns variables provided to the script via HTTP cookies.
* @return array
*/
function getCookies()
{
// We don't have cookies, return empty array
return array();
}
/**
* Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive.
* @return string
*/
function getMethod()
{
// always assume GET
return 'GET';
}
/**
* Checks HTTP request method.
* @param string
* @return bool
*/
function isMethod($method)
{
// always assume GET
return strcasecmp('GET', $method) === 0;
}
/**
* Return the value of the HTTP header. Pass the header name as the
* plain, HTTP-specified header name (e.g. 'Accept-Encoding').
* @param string
* @param mixed
* @return mixed
*/
function getHeader($header, $default = NULL)
{
// no headers
return $default;
}
/**
* Returns all HTTP headers.
* @return array
*/
function getHeaders()
{
// no headers
return array();
}
/**
* Is the request is sent via secure channel (https).
* @return bool
*/
function isSecured() {
// assume unsecured connection
return FALSE;
}
/**
* Is AJAX request?
* @return bool
*/
function isAjax()
{
// assume normal request
return FALSE;
}
/**
* Returns the IP address of the remote client.
* @return string
*/
function getRemoteAddress()
{
// no remote_addr
return NULL;
}
/**
* Returns the host of the remote client.
* @return string
*/
function getRemoteHost()
{
// no remote_host
return NULL;
}
}
?>
Editoval xificurk (15. 5. 2009 13:04)
- xificurk
- Člen | 121
Ještě jsem si vzpomněl na jednu poznámku: Zatím jsem nevymyslel, jak to vyřešit lépe, ale…
Je nutné mít routy včetně domény, protože jinak to suveréně naparsuje
http://example.com/
na {plink //DefaultPresenter:}
.
Jiným řešením by bylo upravit regulár v helperu pro použití na
konkrétních doménách, ale ty routy mi přišly jako menší zlo.