mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 17:50:22 +00:00
Added Zephir language
This commit is contained in:
557
samples/Zephir/Router.zep
Normal file
557
samples/Zephir/Router.zep
Normal file
@@ -0,0 +1,557 @@
|
||||
|
||||
namespace Test\Router;
|
||||
|
||||
/**
|
||||
* Test\Router\Route
|
||||
*
|
||||
* This class represents every route added to the router
|
||||
*/
|
||||
class Route
|
||||
{
|
||||
protected _pattern;
|
||||
|
||||
protected _compiledPattern;
|
||||
|
||||
protected _paths;
|
||||
|
||||
protected _methods;
|
||||
|
||||
protected _hostname;
|
||||
|
||||
protected _converters;
|
||||
|
||||
protected _id;
|
||||
|
||||
protected _name;
|
||||
|
||||
protected _beforeMatch;
|
||||
|
||||
/**
|
||||
* Test\Router\Route constructor
|
||||
*
|
||||
* @param string pattern
|
||||
* @param array paths
|
||||
* @param array|string httpMethods
|
||||
*/
|
||||
public function __construct(pattern, paths=null, httpMethods=null)
|
||||
{
|
||||
// Configure the route (extract parameters, paths, etc)
|
||||
this->reConfigure(pattern, paths);
|
||||
|
||||
// Update the HTTP method constraints
|
||||
let this->_methods = httpMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces placeholders from pattern returning a valid PCRE regular expression
|
||||
*
|
||||
* @param string pattern
|
||||
* @return string
|
||||
*/
|
||||
public function compilePattern(pattern)
|
||||
{
|
||||
var idPattern;
|
||||
|
||||
// If a pattern contains ':', maybe there are placeholders to replace
|
||||
if memstr(pattern, ":") {
|
||||
|
||||
// This is a pattern for valid identifiers
|
||||
let idPattern = "/([a-zA-Z0-9\\_\\-]+)";
|
||||
|
||||
// Replace the module part
|
||||
if memstr(pattern, "/:module") {
|
||||
let pattern = str_replace("/:module", idPattern, pattern);
|
||||
}
|
||||
|
||||
// Replace the controller placeholder
|
||||
if memstr(pattern, "/:controller") {
|
||||
let pattern = str_replace("/:controller", idPattern, pattern);
|
||||
}
|
||||
|
||||
// Replace the namespace placeholder
|
||||
if memstr(pattern, "/:namespace") {
|
||||
let pattern = str_replace("/:namespace", idPattern, pattern);
|
||||
}
|
||||
|
||||
// Replace the action placeholder
|
||||
if memstr(pattern, "/:action") {
|
||||
let pattern = str_replace("/:action", idPattern, pattern);
|
||||
}
|
||||
|
||||
// Replace the params placeholder
|
||||
if memstr(pattern, "/:params") {
|
||||
let pattern = str_replace("/:params", "(/.*)*", pattern);
|
||||
}
|
||||
|
||||
// Replace the int placeholder
|
||||
if memstr(pattern, "/:int") {
|
||||
let pattern = str_replace("/:int", "/([0-9]+)", pattern);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the pattern has parantheses in order to add the regex delimiters
|
||||
if memstr(pattern, "(") {
|
||||
return "#^" . pattern . "$#";
|
||||
}
|
||||
|
||||
// Square brackets are also checked
|
||||
if memstr(pattern, "[") {
|
||||
return "#^" . pattern . "$#";
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more HTTP methods that constraint the matching of the route
|
||||
*
|
||||
*<code>
|
||||
* $route->via('GET');
|
||||
* $route->via(array('GET', 'POST'));
|
||||
*</code>
|
||||
*
|
||||
* @param string|array httpMethods
|
||||
* @return Test\Router\Route
|
||||
*/
|
||||
public function via(httpMethods)
|
||||
{
|
||||
let this->_methods = httpMethods;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts parameters from a string
|
||||
*
|
||||
* @param string pattern
|
||||
*/
|
||||
public function extractNamedParams(string pattern)
|
||||
{
|
||||
|
||||
char ch;
|
||||
var tmp, matches;
|
||||
boolean notValid = false;
|
||||
int cursor, cursorVar, marker, bracketCount = 0, parenthesesCount = 0, foundPattern = 0;
|
||||
int intermediate = 0, numberMatches = 0;
|
||||
string route, item, variable, regexp;
|
||||
|
||||
if strlen(pattern) <= 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let matches = [],
|
||||
route = "";
|
||||
|
||||
for cursor, ch in pattern {
|
||||
|
||||
if parenthesesCount == 0 {
|
||||
if ch == '{' {
|
||||
if bracketCount == 0 {
|
||||
let marker = cursor + 1,
|
||||
intermediate = 0,
|
||||
notValid = false;
|
||||
}
|
||||
let bracketCount++;
|
||||
} else {
|
||||
if ch == '}' {
|
||||
let bracketCount--;
|
||||
if intermediate > 0 {
|
||||
if bracketCount == 0 {
|
||||
|
||||
let numberMatches++,
|
||||
variable = null,
|
||||
regexp = null,
|
||||
item = (string) substr(pattern, marker, cursor - marker);
|
||||
|
||||
for cursorVar, ch in item {
|
||||
|
||||
if ch == '\0' {
|
||||
break;
|
||||
}
|
||||
|
||||
if cursorVar == 0 && !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
|
||||
let notValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <='9') || ch == '-' || ch == '_' || ch == ':' {
|
||||
if ch == ':' {
|
||||
let variable = (string) substr(item, 0, cursorVar),
|
||||
regexp = (string) substr(item, cursorVar + 1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
let notValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !notValid {
|
||||
|
||||
let tmp = numberMatches;
|
||||
|
||||
if variable && regexp {
|
||||
|
||||
let foundPattern = 0;
|
||||
for ch in regexp {
|
||||
if ch == '\0' {
|
||||
break;
|
||||
}
|
||||
if !foundPattern {
|
||||
if ch == '(' {
|
||||
let foundPattern = 1;
|
||||
}
|
||||
} else {
|
||||
if ch == ')' {
|
||||
let foundPattern = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if foundPattern != 2 {
|
||||
let route .= '(',
|
||||
route .= regexp,
|
||||
route .= ')';
|
||||
} else {
|
||||
let route .= regexp;
|
||||
}
|
||||
let matches[variable] = tmp;
|
||||
} else {
|
||||
let route .= "([^/]*)",
|
||||
matches[item] = tmp;
|
||||
}
|
||||
} else {
|
||||
let route .= "{" . item ."}";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bracketCount == 0 {
|
||||
if ch == '(' {
|
||||
let parenthesesCount++;
|
||||
} else {
|
||||
if ch == ')' {
|
||||
let parenthesesCount--;
|
||||
if parenthesesCount == 0 {
|
||||
let numberMatches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bracketCount > 0 {
|
||||
let intermediate++;
|
||||
} else {
|
||||
let route .= ch;
|
||||
}
|
||||
}
|
||||
|
||||
return [route, matches];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigure the route adding a new pattern and a set of paths
|
||||
*
|
||||
* @param string pattern
|
||||
* @param array paths
|
||||
*/
|
||||
public function reConfigure(pattern, paths=null)
|
||||
{
|
||||
var moduleName, controllerName, actionName,
|
||||
parts, routePaths, realClassName, namespaceName,
|
||||
pcrePattern, compiledPattern, extracted;
|
||||
|
||||
if typeof pattern != "string" {
|
||||
throw new Exception("The pattern must be string");
|
||||
}
|
||||
|
||||
if paths !== null {
|
||||
if typeof paths == "string" {
|
||||
|
||||
let moduleName = null,
|
||||
controllerName = null,
|
||||
actionName = null;
|
||||
|
||||
// Explode the short paths using the :: separator
|
||||
let parts = explode("::", paths);
|
||||
|
||||
// Create the array paths dynamically
|
||||
switch count(parts) {
|
||||
case 3:
|
||||
let moduleName = parts[0],
|
||||
controllerName = parts[1],
|
||||
actionName = parts[2];
|
||||
break;
|
||||
case 2:
|
||||
let controllerName = parts[0],
|
||||
actionName = parts[1];
|
||||
break;
|
||||
case 1:
|
||||
let controllerName = parts[0];
|
||||
break;
|
||||
}
|
||||
|
||||
let routePaths = [];
|
||||
|
||||
// Process module name
|
||||
if moduleName !== null {
|
||||
let routePaths["module"] = moduleName;
|
||||
}
|
||||
|
||||
// Process controller name
|
||||
if controllerName !== null {
|
||||
|
||||
// Check if we need to obtain the namespace
|
||||
if memstr(controllerName, "\\") {
|
||||
|
||||
// Extract the real class name from the namespaced class
|
||||
let realClassName = get_class_ns(controllerName);
|
||||
|
||||
// Extract the namespace from the namespaced class
|
||||
let namespaceName = get_ns_class(controllerName);
|
||||
|
||||
// Update the namespace
|
||||
if namespaceName {
|
||||
let routePaths["namespace"] = namespaceName;
|
||||
}
|
||||
} else {
|
||||
let realClassName = controllerName;
|
||||
}
|
||||
|
||||
// Always pass the controller to lowercase
|
||||
let routePaths["controller"] = uncamelize(realClassName);
|
||||
}
|
||||
|
||||
// Process action name
|
||||
if actionName !== null {
|
||||
let routePaths["action"] = actionName;
|
||||
}
|
||||
} else {
|
||||
let routePaths = paths;
|
||||
}
|
||||
} else {
|
||||
let routePaths = [];
|
||||
}
|
||||
|
||||
if typeof routePaths !== "array" {
|
||||
throw new Exception("The route contains invalid paths");
|
||||
}
|
||||
|
||||
// If the route starts with '#' we assume that it is a regular expression
|
||||
if !starts_with(pattern, "#") {
|
||||
|
||||
if memstr(pattern, "{") {
|
||||
// The route has named parameters so we need to extract them
|
||||
let extracted = this->extractNamedParams(pattern),
|
||||
pcrePattern = extracted[0],
|
||||
routePaths = array_merge(routePaths, extracted[1]);
|
||||
} else {
|
||||
let pcrePattern = pattern;
|
||||
}
|
||||
|
||||
// Transform the route's pattern to a regular expression
|
||||
let compiledPattern = this->compilePattern(pcrePattern);
|
||||
} else {
|
||||
let compiledPattern = pattern;
|
||||
}
|
||||
|
||||
// Update the original pattern
|
||||
let this->_pattern = pattern;
|
||||
|
||||
// Update the compiled pattern
|
||||
let this->_compiledPattern = compiledPattern;
|
||||
|
||||
//Update the route's paths
|
||||
let this->_paths = routePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route's name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the route's name
|
||||
*
|
||||
*<code>
|
||||
* $router->add('/about', array(
|
||||
* 'controller' => 'about'
|
||||
* ))->setName('about');
|
||||
*</code>
|
||||
*
|
||||
* @param string name
|
||||
* @return Route
|
||||
*/
|
||||
public function setName(name)
|
||||
{
|
||||
let this->_name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a callback that is called if the route is matched.
|
||||
* The developer can implement any arbitrary conditions here
|
||||
* If the callback returns false the route is treaded as not matched
|
||||
*
|
||||
* @param callback callback
|
||||
* @return Test\Router\Route
|
||||
*/
|
||||
public function beforeMatch(callback)
|
||||
{
|
||||
let this->_beforeMatch = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'before match' callback if any
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getBeforeMatch()
|
||||
{
|
||||
return this->_beforeMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route's id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRouteId()
|
||||
{
|
||||
return this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route's pattern
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPattern()
|
||||
{
|
||||
return this->_pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route's compiled pattern
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCompiledPattern()
|
||||
{
|
||||
return this->_compiledPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPaths()
|
||||
{
|
||||
return this->_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths using positions as keys and names as values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getReversedPaths()
|
||||
{
|
||||
var reversed, path, position;
|
||||
|
||||
let reversed = [];
|
||||
for path, position in this->_paths {
|
||||
let reversed[position] = path;
|
||||
}
|
||||
return reversed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a set of HTTP methods that constraint the matching of the route (alias of via)
|
||||
*
|
||||
*<code>
|
||||
* $route->setHttpMethods('GET');
|
||||
* $route->setHttpMethods(array('GET', 'POST'));
|
||||
*</code>
|
||||
*
|
||||
* @param string|array httpMethods
|
||||
* @return Test\Router\Route
|
||||
*/
|
||||
public function setHttpMethods(httpMethods)
|
||||
{
|
||||
let this->_methods = httpMethods;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP methods that constraint matching the route
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function getHttpMethods()
|
||||
{
|
||||
return this->_methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a hostname restriction to the route
|
||||
*
|
||||
*<code>
|
||||
* $route->setHostname('localhost');
|
||||
*</code>
|
||||
*
|
||||
* @param string|array httpMethods
|
||||
* @return Test\Router\Route
|
||||
*/
|
||||
public function setHostname(hostname)
|
||||
{
|
||||
let this->_hostname = hostname;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hostname restriction if any
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHostname()
|
||||
{
|
||||
return this->_hostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a converter to perform an additional transformation for certain parameter
|
||||
*
|
||||
* @param string name
|
||||
* @param callable converter
|
||||
* @return Test\Router\Route
|
||||
*/
|
||||
public function convert(name, converter)
|
||||
{
|
||||
let this->_converters[name] = converter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the router converter
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConverters()
|
||||
{
|
||||
return this->_converters;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user