Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for default parameters in Router/URL generation #1880

Merged
merged 2 commits into from Jan 23, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@
- Phalcon\Mvc\Model\Metadata now implements Phalcon\Mvc\Model\MetadataInterface (#1852)
- Phalcon\Mvc\Model\Valdator now implements Phalcon\Mvc\Model\ValidatorInterface (#1852)
- Phalcon\Mvc\View\Engine now implements Phalcon\Mvc\View\EngineInterface (#1852)
- Added support for default parameters in Router/URL generation (#1078)
- Phalcon\Paginator:
- Phalcon\Paginator\Adapter\Model returns correct results even when page number is incorrect (#1654)
- Optimized Phalcon\Paginator\Adapter\QueryBuilder (#1632)
Expand Down
125 changes: 105 additions & 20 deletions ext/kernel/framework/router.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,46 @@ zval *phalcon_replace_marker(int named, zval *paths, zval *replacements, unsigne
return NULL;
}

static void phalcon_append_params_to_url(zval *params, smart_str *route_str)
{
HashPosition pos;
HashTable *ht;
zval **v, v_copy;
zval *v_copy_ptr = &v_copy;

assert(Z_TYPE_P(params) == IS_ARRAY);
ht = Z_ARRVAL_P(params);

for (
zend_hash_internal_pointer_reset_ex(ht, &pos);
zend_hash_get_current_data_ex(ht, (void**)&v, &pos) == SUCCESS;
) {
int use_copy = 0;

if (Z_TYPE_PP(v) != IS_STRING) {
zend_make_printable_zval(*v, &v_copy, &use_copy);
if (use_copy) {
v = &v_copy_ptr;
}
}

smart_str_appendl(route_str, Z_STRVAL_PP(v), Z_STRLEN_PP(v));

if (use_copy) {
zval_dtor(&v_copy);
}

zend_hash_move_forward_ex(ht, &pos);
if (pos) {
smart_str_appendc(route_str, '/');
}
}
}

/**
* Replaces placeholders and named variables with their corresponding values in an array
*/
void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval *replacements TSRMLS_DC){
void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval *replacements, zval *defaults TSRMLS_DC){

char *cursor, *marker = NULL;
unsigned int bracket_count = 0, parentheses_count = 0, intermediate = 0;
Expand All @@ -114,6 +150,8 @@ void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval
int i;
zval *replace, replace_copy;
int use_copy, looking_placeholder = 0;
int cut_url_at = 0;
int defaults_used = 0;

if (Z_TYPE_P(pattern) != IS_STRING || Z_TYPE_P(replacements) != IS_ARRAY || Z_TYPE_P(paths) != IS_ARRAY) {
ZVAL_NULL(return_value);
Expand All @@ -136,7 +174,7 @@ void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval
/**
* Ignoring the first character, it must be a /
*/
cursor++;
cursor++;

for (i = 1; i < Z_STRLEN_P(pattern); i++) {

Expand All @@ -158,17 +196,31 @@ void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval
if (intermediate > 0) {
if (bracket_count == 0) {
replace = phalcon_replace_marker(1, paths, replacements, &position, cursor, marker);
if (!replace && (defaults != NULL)) {
position--;
replace = phalcon_replace_marker(1, paths, defaults, &position, cursor, marker);
defaults_used = 1;
} else {
defaults_used = 0;
}
if (replace) {
use_copy = 0;
if (Z_TYPE_P(replace) != IS_STRING) {
zend_make_printable_zval(replace, &replace_copy, &use_copy);
if (Z_TYPE_P(replace) == IS_ARRAY) {
phalcon_append_params_to_url(replace, &route_str);
} else {
if (Z_TYPE_P(replace) != IS_STRING) {
zend_make_printable_zval(replace, &replace_copy, &use_copy);
if (use_copy) {
replace = &replace_copy;
}
}
smart_str_appendl(&route_str, Z_STRVAL_P(replace), Z_STRLEN_P(replace));
if (use_copy) {
replace = &replace_copy;
zval_dtor(&replace_copy);
}
}
smart_str_appendl(&route_str, Z_STRVAL_P(replace), Z_STRLEN_P(replace));
if (use_copy) {
zval_dtor(&replace_copy);
if (!defaults_used) {
cut_url_at = route_str.len;
}
}
cursor++;
Expand All @@ -192,17 +244,31 @@ void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval
if (intermediate > 0) {
if (parentheses_count == 0) {
replace = phalcon_replace_marker(0, paths, replacements, &position, cursor, marker);
if (!replace && (defaults != NULL)) {
position--;
replace = phalcon_replace_marker(1, paths, defaults, &position, cursor, marker);
defaults_used = 1;
} else {
defaults_used = 0;
}
if (replace) {
use_copy = 0;
if (Z_TYPE_P(replace) != IS_STRING) {
zend_make_printable_zval(replace, &replace_copy, &use_copy);
if (Z_TYPE_P(replace) == IS_ARRAY) {
phalcon_append_params_to_url(replace, &route_str);
} else {
if (Z_TYPE_P(replace) != IS_STRING) {
zend_make_printable_zval(replace, &replace_copy, &use_copy);
if (use_copy) {
replace = &replace_copy;
}
}
smart_str_appendl(&route_str, Z_STRVAL_P(replace), Z_STRLEN_P(replace));
if (use_copy) {
replace = &replace_copy;
zval_dtor(&replace_copy);
}
}
smart_str_appendl(&route_str, Z_STRVAL_P(replace), Z_STRLEN_P(replace));
if (use_copy) {
zval_dtor(&replace_copy);
if (!defaults_used) {
cut_url_at = route_str.len;
}
}
cursor++;
Expand All @@ -218,20 +284,38 @@ void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval
if (intermediate > 0) {
if (ch < 'a' || ch > 'z' || i == (Z_STRLEN_P(pattern) - 1)) {
replace = phalcon_replace_marker(0, paths, replacements, &position, cursor, marker);
if (!replace && (defaults != NULL)) {
position--;
replace = phalcon_replace_marker(1, paths, defaults, &position, cursor, marker);
defaults_used = 1;
} else {
defaults_used = 0;
}
if (replace) {
use_copy = 0;
if (Z_TYPE_P(replace) != IS_STRING) {
zend_make_printable_zval(replace, &replace_copy, &use_copy);
if (Z_TYPE_P(replace) == IS_ARRAY) {
phalcon_append_params_to_url(replace, &route_str);
} else {
if (Z_TYPE_P(replace) != IS_STRING) {
zend_make_printable_zval(replace, &replace_copy, &use_copy);
if (use_copy) {
replace = &replace_copy;
}
}
smart_str_appendl(&route_str, Z_STRVAL_P(replace), Z_STRLEN_P(replace));
if (use_copy) {
replace = &replace_copy;
zval_dtor(&replace_copy);
}
}
smart_str_appendl(&route_str, Z_STRVAL_P(replace), Z_STRLEN_P(replace));
if (use_copy) {
zval_dtor(&replace_copy);
if (!defaults_used) {
cut_url_at = route_str.len;
}
}
looking_placeholder = 0;
if (i < (Z_STRLEN_P(pattern) - 1)) {
--i;
}

continue;
}
}
Expand All @@ -252,6 +336,7 @@ void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval

cursor++;
}
route_str.len = cut_url_at;
smart_str_0(&route_str);

if (route_str.len) {
Expand Down
13 changes: 10 additions & 3 deletions ext/kernel/framework/router.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
+------------------------------------------------------------------------+
*/

/** Extract named parameters */
extern void phalcon_extract_named_params(zval *return_value, zval *str, zval *matches);
extern void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval *uri TSRMLS_DC);
#ifndef PHALCON_KERNEL_FRAMEWORK_ROUTER_H
#define PHALCON_KERNEL_FRAMEWORK_ROUTER_H

#include <Zend/zend.h>

/* Extract named parameters */
void phalcon_extract_named_params(zval *return_value, zval *str, zval *matches);
void phalcon_replace_paths(zval *return_value, zval *pattern, zval *paths, zval *uri, zval *defaults TSRMLS_DC);

#endif /* PHALCON_KERNEL_FRAMEWORK_ROUTER_H */
49 changes: 41 additions & 8 deletions ext/mvc/url.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,18 +295,23 @@ PHP_METHOD(Phalcon_Mvc_Url, get){
zval *service, *route_name, *route, *exception_message;
zval *pattern, *paths, *processed_uri, *args = NULL, *query_string;

zval *namespace_name, *module_name;
zval *controller_name, *action_name;

zval *defaults;

PHALCON_MM_GROW();

phalcon_fetch_params(1, 0, 2, &uri, &args);

if (!uri) {
uri = PHALCON_GLOBAL(z_null);
}

PHALCON_INIT_VAR(base_uri);
phalcon_call_method(base_uri, this_ptr, "getbaseuri");
if (Z_TYPE_P(uri) == IS_ARRAY) {
if (!phalcon_array_isset_string(uri, SS("for"))) {
if (!phalcon_array_isset_string_fetch(&route_name, uri, SS("for"))) {
PHALCON_THROW_EXCEPTION_STR(phalcon_mvc_url_exception_ce, "It's necessary to define the route name with the parameter \"for\"");
return;
}
Expand Down Expand Up @@ -334,9 +339,37 @@ PHP_METHOD(Phalcon_Mvc_Url, get){
PHALCON_VERIFY_INTERFACE(router, phalcon_mvc_routerinterface_ce);
phalcon_update_property_this(this_ptr, SL("_router"), router TSRMLS_CC);
}

PHALCON_OBS_VAR(route_name);
phalcon_array_fetch_string(&route_name, uri, SL("for"), PH_NOISY);

PHALCON_INIT_VAR(defaults);
array_init_size(defaults, 4);

if (!phalcon_array_isset_string_fetch(&namespace_name, uri, SS("namespace"))) {
namespace_name = phalcon_fetch_nproperty_this(router, SL("_defaultNamespace"), PH_NOISY_CC);
if (Z_TYPE_P(namespace_name) != IS_NULL) {
phalcon_array_update_string(&defaults, SL("namespace"), &namespace_name, PH_COPY);
}
}

if (!phalcon_array_isset_string_fetch(&module_name, uri, SS("module"))) {
module_name = phalcon_fetch_nproperty_this(router, SL("_defaultModule"), PH_NOISY_CC);
if (Z_TYPE_P(module_name) != IS_NULL) {
phalcon_array_update_string(&defaults, SL("module"), &module_name, PH_COPY);
}
}

if (!phalcon_array_isset_string_fetch(&controller_name, uri, SS("controller"))) {
controller_name = phalcon_fetch_nproperty_this(router, SL("_defaultController"), PH_NOISY_CC);
if (Z_TYPE_P(controller_name) != IS_NULL) {
phalcon_array_update_string(&defaults, SL("controller"), &controller_name, PH_COPY);
}
}

if (!phalcon_array_isset_string_fetch(&action_name, uri, SS("action"))) {
action_name = phalcon_fetch_nproperty_this(router, SL("_defaultAction"), PH_NOISY_CC);
if (Z_TYPE_P(action_name) != IS_NULL) {
phalcon_array_update_string(&defaults, SL("action"), &action_name, PH_COPY);
}
}

/**
* Every route is uniquely differenced by a name
Expand All @@ -349,7 +382,7 @@ PHP_METHOD(Phalcon_Mvc_Url, get){
PHALCON_THROW_EXCEPTION_ZVAL(phalcon_mvc_url_exception_ce, exception_message);
return;
}

PHALCON_INIT_VAR(pattern);
phalcon_call_method(pattern, route, "getpattern");

Expand All @@ -358,12 +391,12 @@ PHP_METHOD(Phalcon_Mvc_Url, get){
*/
PHALCON_INIT_VAR(paths);
phalcon_call_method(paths, route, "getreversedpaths");

/**
* Replace the patterns by its variables
*/
PHALCON_INIT_VAR(processed_uri);
phalcon_replace_paths(processed_uri, pattern, paths, uri TSRMLS_CC);
phalcon_replace_paths(processed_uri, pattern, paths, uri, defaults TSRMLS_CC);
PHALCON_CONCAT_VV(return_value, base_uri, processed_uri);
}
else {
Expand Down
49 changes: 49 additions & 0 deletions ext/tests/issue-1078.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
Support default parameters in Router/URL generation - https://github.com/phalcon/cphalcon/issues/1078
--SKIPIF--
<?php include('skipif.inc'); ?>
--FILE--
<?php
$di = new \Phalcon\DI\FactoryDefault();

$defController = 'home';
$defAction = 'index';

$router = $di['router'];
$router->removeExtraSlashes(true)
->setDefaultNamespace('My\Controllers')
->setDefaultModule('public')
->setDefaultController($defController)
->setDefaultAction($defAction);

$router->add('/:controller/:action/:params', array(
'controller' => 1,
'action' => 2,
'params' => 3
))->setName('secured');

$offset = '/offset/';

$testData = array(
'secured' => array(
array('params' => null),
array('params' => array()),
array('params' => array('params' => array('a', 1, 'b', 'c'))),
array('params' => array('params' => array())),
),
);

foreach ($testData as $routeName => $cases) {
foreach ($cases as $idx => $case) {
$url = $di['url'];
$url->setBaseUri($offset);
$params = (array)$case['params'] + array('for' => $routeName);
echo $url->get($params), PHP_EOL;
}
}
?>
--EXPECT--
/offset/
/offset/
/offset/home/index/a/1/b/c
/offset/home/index/