当前位置: 首页 > 工具软件 > Phalcon > 使用案例 >

关于 PHP 的框架 phalcon 学习(一) url 路由过程。

班思源
2023-12-01

phalcon 这个轮子 都说不错,可是 对于 看惯了 python源码的人 ,还是很有意思得,下面是对phalcon 的一点理解 跟 一点不完全的代码。项目 是用 phalcon-tool 命令行自动生成的。

框架的 入口文件 是public 下 的index.php, 具体内容如下,

try {

    /**
     * The FactoryDefault Dependency Injector automatically registers
     * the services that provide a full stack framework.
     */
    $di = new FactoryDefault();

    /**
     * Handle routes
     */
    include APP_PATH . '/config/router.php';

    /**
     * Read services
     */
    include APP_PATH . '/config/services.php';

    /**
     * Get config service for use in inline setup below
     */
    $config = $di->getConfig();

    /**
     * Include Autoloader
     */
    include APP_PATH . '/config/loader.php';

    /**
     * Handle the request
     */
    $application = new \Phalcon\Mvc\Application($di);

这个 文件 主要主要做了 几个事情,实例化了 FactoryDefault , 这个 东西主要是管理 各种对象 的容器。下面include 跟的是 各种组件的路径,会被扫包 。对 application 注入 容器 ,处理request 请求,当然这是很初级的一个 启动方式,你可以 把一些 公共服务  比如 router ,session ,validator等 服务 ,统一写在一个配置文件里,方便清晰。

router  路由 需要自己 定义,在 程序开始 就被 注入容器,成为一个 服务,对于 url 请求 路由  是通过 router handle 方法处理,具体源码如下。

public function handle(string! uri)
	{
		var request, currentHostName, routeFound, parts,
			params, matches, notFoundPaths,
			vnamespace, module,  controller, action, paramsStr, strParams,
			route, methods, dependencyInjector,
			hostname, regexHostName, matched, pattern, handledUri, beforeMatch,
			paths, converters, part, position, matchPosition, converter, eventsManager;

		/**
		 * Remove extra slashes in the route
		 */
		if this->_removeExtraSlashes && uri != "/" {
			let handledUri = rtrim(uri, "/");
		} else {
			let handledUri = uri;
		}

		let request = null,
			currentHostName = null,
			routeFound = false,
			parts = [],
			params = [],
			matches = null,
			this->_wasMatched = false,
			this->_matchedRoute = null;

		let eventsManager = this->_eventsManager;

		if typeof eventsManager == "object" {
			eventsManager->fire("router:beforeCheckRoutes", this);
		}

		/**
		 * Routes are traversed in reversed order
		 */
		for route in reverse this->_routes {
			let params = [],
				matches = null;

			/**
			 * Look for HTTP method constraints
			 */
			let methods = route->getHttpMethods();
			if methods !== null {

				/**
				 * Retrieve the request service from the container
				 */
				if request === null {

					let dependencyInjector = <DiInterface> this->_dependencyInjector;
					if typeof dependencyInjector != "object" {
						throw new Exception("A dependency injection container is required to access the 'request' service");
					}

					let request = <RequestInterface> dependencyInjector->getShared("request");
				}

				/**
				 * Check if the current method is allowed by the route
				 */
				if request->isMethod(methods, true) === false {
					continue;
				}
			}

			/**
			 * Look for hostname constraints
			 */
			let hostname = route->getHostName();
			if hostname !== null {

				/**
				 * Retrieve the request service from the container
				 */
				if request === null {

					let dependencyInjector = <DiInterface> this->_dependencyInjector;
					if typeof dependencyInjector != "object" {
						throw new Exception("A dependency injection container is required to access the 'request' service");
					}

					let request = <RequestInterface> dependencyInjector->getShared("request");
				}

				/**
				 * Check if the current hostname is the same as the route
				 */
				if typeof currentHostName == "null" {
					let currentHostName = request->getHttpHost();
				}

				/**
				 * No HTTP_HOST, maybe in CLI mode?
				 */
				if !currentHostName {
					continue;
				}

				/**
				 * Check if the hostname restriction is the same as the current in the route
				 */
				if memstr(hostname, "(") {
					if !memstr(hostname, "#") {
						let regexHostName = "#^" . hostname;
						if !memstr(hostname, ":") {
							let regexHostName .= "(:[[:digit:]]+)?";
						}
						let regexHostName .= "$#i";
					} else {
						let regexHostName = hostname;
					}
					let matched = preg_match(regexHostName, currentHostName);
				} else {
					let matched = currentHostName == hostname;
				}

				if !matched {
					continue;
				}
			}

			if typeof eventsManager == "object" {
				eventsManager->fire("router:beforeCheckRoute", this, route);
			}

			/**
			 * If the route has parentheses use preg_match
			 */
			let pattern = route->getCompiledPattern();

			if memstr(pattern, "^") {
				let routeFound = preg_match(pattern, handledUri, matches);
			} else {
				let routeFound = pattern == handledUri;
			}

			/**
			 * Check for beforeMatch conditions
			 */
			if routeFound {

				if typeof eventsManager == "object" {
					eventsManager->fire("router:matchedRoute", this, route);
				}

				let beforeMatch = route->getBeforeMatch();
				if beforeMatch !== null {

					/**
					 * Check first if the callback is callable
					 */
					if !is_callable(beforeMatch) {
						throw new Exception("Before-Match callback is not callable in matched route");
					}

					/**
					 * Check first if the callback is callable
					 */
					let routeFound = call_user_func_array(beforeMatch, [handledUri, route, this]);
				}

			} else {
				if typeof eventsManager == "object" {
					let routeFound = eventsManager->fire("router:notMatchedRoute", this, route);
				}
			}

			if routeFound {

				/**
				 * Start from the default paths
				 */
				let paths = route->getPaths(),
					parts = paths;

				/**
				 * Check if the matches has variables
				 */
				if typeof matches == "array" {

					/**
					 * Get the route converters if any
					 */
					let converters = route->getConverters();

					for part, position in paths {

						if typeof part != "string" {
							throw new Exception("Wrong key in paths: " . part);
						}

						if typeof position != "string" && typeof position != "integer" {
							continue;
						}

						if fetch matchPosition, matches[position] {

							/**
							 * Check if the part has a converter
							 */
							if typeof converters == "array" {
								if fetch converter, converters[part] {
									let parts[part] = call_user_func_array(converter, [matchPosition]);
									continue;
								}
							}

							/**
							 * Update the parts if there is no converter
							 */
							let parts[part] = matchPosition;
						} else {

							/**
							 * Apply the converters anyway
							 */
							if typeof converters == "array" {
								if fetch converter, converters[part] {
									let parts[part] = call_user_func_array(converter, [position]);
								}
							} else {

								/**
								 * Remove the path if the parameter was not matched
								 */
								if typeof position == "integer" {
									unset parts[part];
								}
							}
						}
					}

					/**
					 * Update the matches generated by preg_match
					 */
					let this->_matches = matches;
				}

				let this->_matchedRoute = route;
				break;
			}
		}

		/**
		 * Update the wasMatched property indicating if the route was matched
		 */
		if routeFound {
			let this->_wasMatched = true;
		} else {
			let this->_wasMatched = false;
		}

		/**
		 * The route wasn't found, try to use the not-found paths
		 */
		if !routeFound {
			let notFoundPaths = this->_notFoundPaths;
			if notFoundPaths !== null {
				let parts = Route::getRoutePaths(notFoundPaths),
					routeFound = true;
			}
		}

		/**
		 * Use default values before we overwrite them if the route is matched
		 */
		let this->_namespace = this->_defaultNamespace,
			this->_module = this->_defaultModule,
			this->_controller = this->_defaultController,
			this->_action = this->_defaultAction,
			this->_params = this->_defaultParams;

		if routeFound {

			/**
			 * Check for a namespace
			 */
			if fetch vnamespace, parts["namespace"] {
				if !is_numeric(vnamespace) {
					let this->_namespace = vnamespace;
				}
				unset parts["namespace"];
			}

			/**
			 * Check for a module
			 */
			if fetch module, parts["module"] {
				if !is_numeric(module) {
					let this->_module = module;
				}
				unset parts["module"];
			}

			/**
			 * Check for a controller
			 */
			if fetch controller, parts["controller"] {
				if !is_numeric(controller) {
					let this->_controller = controller;
				}
				unset parts["controller"];
			}

			/**
			 * Check for an action
			 */
			if fetch action, parts["action"] {
				if !is_numeric(action) {
					let this->_action = action;
				}
				unset parts["action"];
			}

			/**
			 * Check for parameters
			 */
			if fetch paramsStr, parts["params"] {
				if typeof paramsStr == "string" {
					let strParams = trim(paramsStr, "/");
					if strParams !== "" {
						let params = explode("/", strParams);
					}
				}

				unset parts["params"];
			}

			if count(params) {
				let this->_params = array_merge(params, parts);
			} else {
				let this->_params = parts;
			}
		}

		if typeof eventsManager == "object" {
			eventsManager->fire("router:afterCheckRoutes", this);
		}
	}

这么多 ,前面一大堆 判断,用处大得就两个 方法,getCompiledPattern 取出 uri  所有样式,然后调用 preg_match 方法来比较 请求 得uri 跟 自己设置得uri  样式是否一致,一致得 话 就 调用checkcontroller 方法 跟 checkaction  方法,找出正确得 视图 跟 方 action。

当 uri 的 request 来到正确的 controller 跟 action ,就该调用 model 层 跟 数据库 交互。常见的是这种交互方式,基于视图的交互方式,贴出源码 

 $this -> view -> setVars(array(
            'categoryList' => $categoryList,
            'article' => $article,
        ));
        $this -> view -> pick('articles/write');
    }

view  服务 是 自己 找到 view视图类 文件夹,然后注入容器的,关键是 这个pick 方法做了 什么?下面贴出源码

public function pick(var renderView) -> <View>
	{
		var pickView, layout, parts;

		if typeof renderView == "array" {
			let pickView = renderView;
		} else {

			let layout = null;
			if memstr(renderView, "/") {
				let parts = explode("/", renderView), layout = parts[0];
			}

			let pickView = [renderView];
			if layout !== null {
				let pickView[] = layout;
			}
		}

		let this->_pickView = pickView;
		return this;

 

这个方法主要作用是 把 查询出的变量 渲染进模板 ,返回给B端。

关于 restfiful 风格接口 一点设想,可以借鉴django

方法URL操作
GET/api/robots检索所有的机器人
GET/api/robots/search/Astro在他们的名字与 'Astro' 机器人搜索
GET/api/robots/2检索基于主键机器人
POST/api/robots添加一个新的机器人
PUT/api/robots/2Updates robots based on primary key
DELETE/api/robots/2删除基于主键机器人

 

好了 ,有时间 明天继续看。

 

 

 

 类似资料: