Skip to content

Instantly share code, notes, and snippets.

@dhaupin
Last active February 25, 2017 02:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhaupin/d4fdfb1f0d86099ca5a286283471b01f to your computer and use it in GitHub Desktop.
Save dhaupin/d4fdfb1f0d86099ca5a286283471b01f to your computer and use it in GitHub Desktop.
A deconstructed get_route() hook for CS-Cart
<?php
// @@@ DEBUG the x_r() function is here: https://gist.github.com/dhaupin/d9d48328dbe312f6c0de
// http://forum.cs-cart.com/topic/47926-seo-addon-301-redirects-making-them-work-when-object-exists-with-same-uri/
/**
* "get_route" hook implemetation
* @param array &$req input request
* @param array &$result result of init function
* @param string $area current working area
* @param boolean $is_allowed_url Flag that determines if url is supported // note that false triggers a redirect
* @return bool true on success, false on failure
*/
function fn_seo_get_route(&$req, &$result, &$area, &$is_allowed_url)
{
if (($area == 'C') && !$is_allowed_url) {
$uri = fn_get_request_uri($_SERVER['REQUEST_URI']);
// /test-redirect.html
//x_r($uri);
if (!empty($uri)) {
$rewrite_rules = fn_get_rewrite_rules();
$i = 0;
//x_r($rewrite_rules);
//x_r(Registry::get('addons.seo.single_url')); // Y
//x_r(Registry::get('addons.seo.seo_language')); // N
// $rewrite_rules
/*
[!^()\/(.*\/)?([^\/]+)-page-([0-9]+|full_list)\.(html)$!] => object_name=$matches[3]&page=$matches[4]&sl=$matches[1]&extension=$matches[5]
[!^()\/(.*\/)?([^\/]+)\.(html)$!] => object_name=$matches[3]&sl=$matches[1]&extension=$matches[4]
[!^()\/(.*\/)?([^\/]+)\/page-([0-9]+|full_list)(\/)?$!] => object_name=$matches[3]&page=$matches[4]&sl=$matches[1]
[!^()\/(.*\/)?([^\/?]+)\/?$!] => object_name=$matches[3]&sl=$matches[1]
[!^()/$!] =>
*/
foreach ($rewrite_rules as $pattern => $query) {
// increase counter each loop
//$i++;
if (preg_match($pattern, $uri, $matches) || preg_match($pattern, urldecode($query), $matches)) {
// increase count if it matches
// login.html page name loops twice
// others.html like cats/prods loops once
$i++;
if ($i == 2) {
//x_r($pattern);
//x_r($matches);
}
// login.html loop 1:
// !^()\/(.*\/)?([^\/]+)\.(html)$!
/*
[0] => /login.html
[1] =>
[2] =>
[3] => login
[4] => html
*/
// login.html loop 2:
// !^()\/(.*\/)?([^\/?]+)\/?$!
/*
[0] => /login.html
[1] =>
[2] =>
[3] => login.html
*/
$_query = preg_replace("!^.+\?!", '', $query);
parse_str($_query, $objects);
$result_values = 'matches';
$url_query = '';
if ($i == 1) {
//x_r($objects);
//x_r($matches[3]);
}
//x_r($objects);
//x_r($matches[3]);
// loop 1 - the uri is split with html as seperate entity
/*
[object_name] => $matches[3] // test-redirect
[sl] => $matches[1] // nothing, but i assume would be -en although it doesnt trigger in uri test
[extension] => $matches[4] // html
*/
// loop 2 - the uri is concatated including html
/*
[object_name] => $matches[3]
[sl] => $matches[1]
*/
foreach ($objects as $key => $value) {
preg_match('!^.+\[([0-9])+\]$!', $value, $_id); // matches full some-url[23]
// here we are converting matches[X] into the values
$objects[$key] = (substr($value, 0, 1) == '$') ? ${$result_values}[$_id[1]] : $value;
}
if ($i == 2) {
//x_r($objects);
//x_r($matches[3]);
}
// For the locations wich names stored in the table
if (!empty($objects) && !empty($objects['object_name'])) {
// this inits since we have a name (url)
if (Registry::get('addons.seo.single_url') == 'Y') {
// inits cause "single url for all languages" setting is yes
$objects['sl'] = (Registry::get('addons.seo.seo_language') == 'Y') ? $objects['sl'] : '';
// sl is blank
$objects['sl'] = !empty($req['sl']) ? $req['sl'] : $objects['sl'];
//x_r($req); // req is blank
}
$lang_cond = db_quote("AND lang_code = ?s", !empty($objects['sl']) ? $objects['sl'] : Registry::get('settings.Appearance.frontend_default_language'));
// AND lang_code = 'en'
$object_type = db_get_field("SELECT type FROM ?:seo_names WHERE name = ?s ?p", $objects['object_name'], fn_get_seo_company_condition('?:seo_names.company_id'));
// on pass #2 there is no object type since there is a .html extension
if ($i == 2) {
//exit('pass number ' . $i);
//x_r($object_type);
//x_r($objects['object_name']);
}
//x_r($object_type); // p
/*
types in schemas/seo/objects.php:
c = category (tree)
p = product (tree)
a = page (tree)
e = feature (plain)
s = custom (plain)
*/
$_seo = db_get_array("SELECT * FROM ?:seo_names WHERE name = ?s ?p ?p", $objects['object_name'], fn_get_seo_company_condition('?:seo_names.company_id', $object_type), $lang_cond);
// second loop _seo is still empty
if ($i == 2) {
//exit('pass number ' . $i);
//x_r($_seo);
}
//x_r($_seo);
/*
[name] => test-redirect
[object_id] => 1594 // product_id
[company_id] => 1
[type] => p
[dispatch] =>
[path] => 309 // this is the category_id
[lang_code] => en
*/
// first pass is full, but this inits on second pass
// in the case of a second pass with .html, it prob wont init
if (empty($_seo)) {
//x_r($objects);
//x_r($objects['object_name']);
$_seo = db_get_array("SELECT * FROM ?:seo_names WHERE name = ?s ?p", $objects['object_name'], fn_get_seo_company_condition('?:seo_names.company_id'));
// its searching for pagetitle.html which doesnt exist in seo_names (most lack html)
if ($i == 2) {
//exit('pass number ' . $i);
//x_r($_seo);
}
}
// second loop _seo is still empty
if ($i == 1) {
//exit('pass number ' . $i);
//x_r($_seo);
//x_r($objects);
}
// first loop _seo is array, second loop is not
// first loop has the extension, second does not
// this doesnt init for objects
if (empty($_seo) && !empty($objects['extension'])) {
$_seo = db_get_array("SELECT * FROM ?:seo_names WHERE name = ?s ?p ?p", $objects['object_name'] . '.' . $objects['extension'], fn_get_seo_company_condition('?:seo_names.company_id'), $lang_cond);
//x_r('this should not be happening 2');
if (empty($_seo)) {
$_seo = db_get_array("SELECT * FROM ?:seo_names WHERE name = ?s ?p", $objects['object_name'] . '.' . $objects['extension'], fn_get_seo_company_condition('?:seo_names.company_id', $object_type));
//x_r('this should not be happening 3');
}
}
if ($i == 1) {
//exit('pass number ' . $i);
//x_r($_seo);
//x_r($objects);
}
// _seo loop 1
/*
[0] => Array
(
[name] => test-redirect
[object_id] => 1594
[company_id] => 1
[type] => p
[dispatch] =>
[path] => 309
[lang_code] => en
)
*/
// objects loop 1
/*
[object_name] => test-redirect
[sl] =>
[extension] => html
*/
// objects loop 2
/*
[object_name] => test-redirect.html
[sl] =>
*/
// first loop _seo is array, second loop is not
if (!empty($_seo)) {
// this is a temp trigger, if it runs, the url triggers rewrite somewhere
$_seo_valid = false;
// _seo sub counter
$ii = 0;
foreach ($_seo as $__seo) { // why is this temp
$_objects = $objects;
$ii++;
if (Registry::get('addons.seo.single_url') != 'Y' && empty($_objects['sl'])) {
$_objects['sl'] = $__seo['lang_code'];
// $_objects is not populated with lang code from $_seo
}
if (fn_seo_validate_object($__seo, $uri, $_objects) == true) {
// fn_seo_validate_object returns a 1 or 0
// it runs as many times as there are _seo array children
if ($ii == 1) {
//exit('pass number ' . $i);
//x_r($_seo);
//x_r($objects);
}
$_seo_valid = true;
$_seo = $__seo;
$objects = $_objects;
// break out of this loop when first valid assimilation is found, pass 1
break;
}
} // end foreach ($_seo as $__seo) {
// end $ii
// here after break it does not loop twice
if ($i == 1) {
//exit('pass number ' . $i);
if ($_seo_valid == true) {
//exit('$_seo_valid was true on pass ' . $i);
}
//x_r($_seo);
}
// first pass this is true, there is no second pass
if ($_seo_valid == true) {
$req['sl'] = $objects['sl'];
$_seo_vars = fn_get_seo_vars($_seo['type']);
//x_r(fn_get_seo_vars($_seo['type']));
/*
[tree] => 1
[path_function] => Closure Object
(
[parameter] => Array
(
[$object_id] =>
[$company_id] =>
)
)
[parent_type] => c
[name] => product
[picker] => pickers/products/picker.tpl
[picker_params] => Array
(
[type] => single
[view_mode] => button
)
[table] => ?:product_descriptions
[description] => product
[dispatch] => products.view
[item] => product_id
[condition] =>
[not_shared] => 1
[tree_options] => Array
(
[0] => product_category_nohtml
[1] => product_category
)
[html_options] => Array
(
[0] => product_category
[1] => product_file
)
[option] => seo_product_type
[exist_function] => Closure Object
(
[parameter] => Array
(
[$product_id] =>
[$company_id] =>
)
)
*/
// here we build the dispatch and queries into the $req array
// $req is used later on http_build_query($req); if the url is not redirect needed
if ($_seo['type'] == 's') { // custom seo_name
$url_query = $_seo['dispatch'];
$req['dispatch'] = $_seo['dispatch'];
} else { // if an object was found
//$_seo['type'] object type is p
// page isnt included in objects for products
$page_suffix = (!empty($objects['page'])) ? ('&page=' . $objects['page']) : '';
$url_query = $_seo_vars['dispatch'] . '?' . $_seo_vars['item'] . '=' . $_seo['object_id'] . $page_suffix;
// $url_query => products.view?product_id=1594 // followed by pagination
//x_r($url_query);
$req['dispatch'] = $_seo_vars['dispatch'];
// [dispatch] => products.view
// @@@@ DH here
// set a type for later use?
$_type = $object_type;
// this doesnt really work well:
//$req['type'] = $object_type;
} // end type check
// object id is set in _seo in first pass
// this runs to make =id
if (!empty($_seo['object_id'])) {
$req[$_seo_vars['item']] = $_seo['object_id'];
// $req['product_id'] = 1234;
}
// page is empty for products
if (!empty($objects['page'])) {
$req['page'] = $objects['page'];
}
// @@@@ DH this is normally set without a conditional
// This is only init on first pass, type p
//* @param boolean $is_allowed_url Flag that determines if url is supported // note that false triggers a redirect
/*if ($_type != 's') {
$is_allowed_url = false; // this changes master loops to 2
} else {
$is_allowed_url = true; // 1 master loop
}*/
$is_allowed_url = true; // 1 master loop
//x_r($req);
/*
[sl] =>
[dispatch] => products.view
[product_id] => 1594
*/
} // end if ($_seo_valid == true) { // this was 1 loop, the first
} // end if (!empty($_seo)) { // this was 1 loop, the first
// For the locations wich names are not in the table
} elseif (!empty($objects)) { // end if (!empty($objects) && !empty($objects['object_name'])) {
// this shouldnt init since we have a name
x_r('you shouldnt be seeing me for products');
if (empty($objects['dispatch'])) {
if (!empty($req['dispatch'])) {
$req['dispatch'] = is_array($req['dispatch']) ? key($req['dispatch']) : $req['dispatch'];
$url_query = $req['dispatch'];
}
} else {
$url_query = $objects['dispatch'];
$req['dispatch'] = $objects['dispatch'];
}
$is_allowed_url = true;
if (!empty($objects['sl'])) {
$is_allowed_url = false;
$req['sl'] = $objects['sl'];
if (Registry::get('addons.seo.seo_language') == 'Y') {
$lang_statuses = !empty($_SESSION['auth']['area']) && $_SESSION['auth']['area'] == 'A' ? array('A', 'H') : array('A');
$check_language = db_get_field("SELECT count(*) FROM ?:languages WHERE lang_code = ?s AND status IN (?a)", $req['sl'], $lang_statuses);
if (!empty($check_language)) {
$is_allowed_url = true;
}
} else {
$is_allowed_url = true;
}
}
$req += $objects;
// Empty query
} else { // end } elseif (!empty($objects)) {
$url_query = '';
x_r('you shouldnt be seeing me either');
} // end checks for empty objects
// THERE ARE 2 PASSES HERE NOW
if ($i == 1) {
//exit('pass number ' . $i);
if ($is_allowed_url) {
//exit($_type);
//exit('$is_allowed_url was true on pass ' . $i . ' with type ' . $_type);
}
//x_r($_seo);
}
// this inits on neither pass
// $is_allowed_url of false means that a redirect should happen. this only runs on true (no redirect)
// so this means the break doesnt happen for redirects, hense 2 loops?
// note that if (empty($is_allowed_url)) { reduces total loops to 1
//if (empty($is_allowed_url)) {
if ($is_allowed_url) { // default
$lang_code = empty($objects['sl']) ? Registry::get('settings.Appearance.frontend_default_language') : $objects['sl'];
if (empty($req['sl'])) {
unset($req['sl']);
}
$query_string = http_build_query($req);
//x_r($url_query); // products.view?product_id=1594
//x_r($query_string); // dispatch=products.view&product_id=1594
$_SERVER['REQUEST_URI'] = fn_url($url_query . '?' . $query_string, 'C', 'rel', $lang_code);
// comes out as [REQUEST_URI] => /test-redirect.html
// @@@@ this seems incorrect... fn_url could do products.view?product_id=1594
//x_r($url_query . '?' . $query_string);
// products.view?product_id=1594?dispatch=products.view&product_id=1594
$_SERVER['QUERY_STRING'] = $query_string; // dispatch=products.view&product_id=1594
$_SERVER['X-SEO-REWRITE'] = true; // [X-SEO-REWRITE] => 1
if ($i == 2) {
//x_r('i broke out of master loop at count ' . $i);
//x_r($_SERVER);
}
break; // here is where we break out of regex loops, $i most likely on count 1
} // end if ($is_allowed_url) { // default
} // end if (preg_match($pattern, $uri, $matches) || preg_match($pattern, urldecode($query), $matches)) {
} // end regexd foreach ($rewrite_rules as $pattern => $query) {
// lets spit a loop count
if ($i == 1) {
//x_r('Master loops: ' . $i);
}
} // end if (!empty($uri)) {
} // end if (($area == 'C') && !$is_allowed_url) {
// $i has occured 1 time with defaults
if ($i == 1) {
//x_r('Total loops: ' . $i);
}
// @@@ here is where its not working
// products arent empty $is_allowed_url
// using a type check here causes redir looping, admin side failures, etc
if (empty($is_allowed_url)) { // if not allowed url sound like a backwards
//if (true) {
$query_string = array();
$uri = fn_get_request_uri($_SERVER['REQUEST_URI']);
// Attach additinal params to URI if passed
if (!empty($_SERVER['QUERY_STRING'])) {
parse_str($_SERVER['QUERY_STRING'], $query_string);
}
// Remove pagination from URI
if (preg_match('/\/page-(\d+)\/?$/', $uri, $m)) {
$query_string['page'] = $m[1];
$uri = preg_replace('/\/page-\d+\/?$/', '', $uri);
}
$condition = fn_get_seo_company_condition("?:seo_redirects.company_id");
$redirect_data = db_get_row("SELECT type, object_id, dest, lang_code FROM ?:seo_redirects WHERE src = ?s ?p", $uri, $condition);
//x_r($redirect_data);
/*
[type] => p
[object_id] => 1121 // this is what the redirect target is
[dest] =>
[lang_code] => en
*/
//x_r(fn_generate_seo_url_from_schema($redirect_data, true, $query_string));
if (!empty($redirect_data)) {
$result = array(INIT_STATUS_REDIRECT, fn_generate_seo_url_from_schema($redirect_data, true, $query_string), false, true);
} else {
$req = array(
'dispatch' => '_no_page'
);
}
} // end
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment