Abstract index php app core. Mga query para sa mga static na file

Ang unang artikulo sa pagbuo ng iyong sariling balangkas ay ilalaan sa isang pangkalahatang-ideya ng mga pangunahing nilalayon na kakayahan at mga tampok na arkitektura ng bagong balangkas. Dito ay ilalarawan ko ang mga module at mga bahagi, ang pag-unlad nito ay ilalarawan nang detalyado sa mga susunod na artikulo. Kaya, magpasya tayo kung paano natin inaayos ang code ng framework at mga application na gagamit nito, at pagkatapos ay gagawa tayo ng roadmap para sa pagbuo ng mga pangunahing bahagi.

Upang magsimula, pumili ako ng isang simple at maigsi na istraktura ng direktoryo ng proyekto:

    /
  • app – direktoryo ng webroot server, ilagay ang index.php dito
    • pampubliko - dito mag-iimbak kami ng mga static na file ng application
  • src – direktoryo na may source code ng application mismo
    • Blog – ang bawat application ay matatagpuan sa sarili nitong direktoryo
  • lib – direktoryo para sa mga panlabas na aklatan. Makikita rin dito ang framework code.
  • var – direktoryo para sa mga file ng application (cache, imbakan ng file, mga session, atbp.)

Napagpasyahan namin ang mga direktoryo. Ngayon ay maaari naming maginhawang paghiwalayin ang mga indibidwal na bahagi ng application sa iba't ibang mga direktoryo, gamitin ang PSR0 upang mag-autoload ng mga klase, atbp.
Susunod, kailangan mong piliin ang pangalan ng balangkas upang lumikha lamang ng isang direktoryo para dito sa loob ng lib. Nang walang pag-iisip, pinili ko ang pangalang Bun - maikli, hindi malilimutan at tila hindi sumasalungat sa anumang bagay.

Sa loob ng lib lumikha kami ng isang bun directory (namespace vendor), sa loob nito ay isa pang bun directory (namespace library), at sa loob nito src - dito namin ilalagay ang code ng aming framework. Ang sopistikadong istraktura ng direktoryo ay magbibigay-daan sa amin na ikonekta ang balangkas sa pamamagitan ng http://packagist.org sa hinaharap nang walang mga hindi kinakailangang problema.

Susunod, pumili ng isang namespace para sa mga batayang klase ng balangkas - Pinili ko ang Core. Upang ayusin ang koneksyon ng mga bahagi sa labas ng Core na direktoryo sa hinaharap, kakailanganin mong ayusin ang isang modular na istraktura ng balangkas. Samakatuwid, ang unang bahagi ng system na mayroon ako ay isang module (Bun\Core\Module).

Bun\Core\Module

Ang module ay dapat magbigay ng pangunahing impormasyon sa isang independiyenteng bahagi ng balangkas. Upang magsimula, ang mga pagsasaayos at dependency na ginagamit ng module, bersyon, paglalarawan ng module, atbp. Ang bawat direktoryo sa loob ng lib/bun/bun/src ay magiging isang hiwalay na module at dapat maglaman ng klase ng parehong pangalan (halimbawa, Bun\Core\Core.php) - na kumakatawan sa pagpapatupad ng module.

Bun\Core\Application

Ang application ay isang pagpapatupad ng pattern ng Front Controller - isang solong entry point sa application para sa lahat ng mga kahilingan. Ibibigay ng arkitektura ng Bun na ang lahat ng mga kahilingan, kabilang ang php cli o mga kahilingan para sa mga static na file, ay dapat iproseso sa loob ng Application. Ang isang application instance ay gagawin sa index.php (start file), at kapag ang application ay nasimulan, ang execution environment nito (production, development, testing, etc) ay ipapahiwatig. Gagawin ng application ang gawain ng paglo-load ng configuration, pagsisimula ng service container, pagruruta ng mga kahilingan sa pamamagitan ng mga auxiliary na bahagi, pagtawag sa mga controller, pag-isyu ng tugon, at pagwawakas sa application.

Ang unang bagay na ginagawa namin kapag nagsimula ang isang application, bilang panuntunan, ay ang pag-load ng mga configuration file, kaya ang susunod na pangunahing bahagi ay Config

Bun\Core\Config

Upang i-configure ang mga bahagi ng application at framework, isang hiwalay na hanay ng mga klase ang inilalaan - Bun\Core\Config\ApplicationConfig - ang serbisyo mismo para sa pamamahala ng mga config at ang Bun\Core\Config\AbstractConfig na klase - isang abstract na klase na ang batayang klase para sa mga elemento ng pagsasaayos. Ang klase ng PHP ay pinili bilang format ng pagsasaayos. Ito ay maginhawa mula sa isang caching point of view, i.e. Mas kumikitang mag-imbak ng mga config nang direkta sa application code kaysa gumamit ng hiwalay na mga file sa xml, json, ini, atbp. na mga format.

Ang paggamit ng mga klase bilang mga config ay maginhawa din para sa paghihiwalay ng mga pagsasaayos ng mga indibidwal na bahagi at bahagi ng application. Maginhawa rin ito para sa pag-override sa mga default na setting ng framework sa loob ng isang application, o mga setting ng application sa loob ng isang partikular na kapaligiran ng pagpapatupad. Sa pamamagitan ng disenyo, ang bawat module o application ay naglalaman sa loob mismo ng Config namespace - kung saan naka-imbak ang mga klase ng configuration. Ang bawat klase ay may sariling namespace, at nag-iimbak ng mga parameter ng configuration bilang array sa isang protektadong property.
Ang access sa mga config ay ipinapalagay sa pamamagitan ng dot notation $config->get("name1.name2.param1") .

Pagkatapos naming masimulan ang configuration ng application, maaari na naming simulan ang pagproseso ng kahilingan. Ang isang hiwalay na hanay ng mga bahagi ng Http ay inilalaan para sa pagtatrabaho sa mga kahilingan at tugon mula sa mga web application



Bun\Core\Http

Ang hanay ng mga bahagi ng Http ay magiging responsable para sa pag-abstract mula sa pagtatrabaho sa mga superglobal na variable na $_SERVER, $_GET, $_POST, atbp. Ang serbisyo ng Bun\Core\Http\Request ang mananagot para dito. Bilang karagdagan, ang Http ay magsasama ng isang klase ng Pagtugon - na siyang magiging pamantayan para sa resulta ng aplikasyon, i.e. Ang paglulunsad ng controller ay dapat magtapos sa pagtanggap ng bagay na Bun\Core\Http\Response. Ang klase na ito ay nag-abstract sa amin mula sa pagtatrabaho sa mga header ng http, atbp. Bilang karagdagan, ito ay maginhawang gumamit ng mga nagmula na klase tulad ng AjaxResponse, DownloadResponse, ConsoleResponse, atbp.

Kapag handa na kaming tumanggap ng impormasyon tungkol sa kahilingan, maaari kaming magpatuloy sa pagruruta: ang susunod na bahagi ay ang Router

Bun\Core\Router

Ang router ay isang karaniwang bahagi ng modernong PHP web application. Walang extra ordinary sa Bun Framework router, isang simpleng config sa anyo ng isang hanay ng mga template ng kahilingan ng URL, na naka-map sa mga klase ng controller at sa kanilang mga aksyon. Plano kong ipatupad ang kakayahang mag-parse ng mga parameter mula sa isang url tulad ng /page/view/:page_id - na ipapasa sa pagkilos ng controller bilang mga argumento. Plano ko ring paghiwalayin ang mga kahilingan ayon sa pamamaraan (maginhawa kapag ang ilang mga pamamaraan ay matatawag lamang sa pamamagitan ng POST - hindi na kailangang gumawa ng mga hindi kinakailangang pagsusuri sa logic code ng negosyo)

Lumipat kami mula sa isang karaniwang bahagi ng mga application ng PHP patungo sa isa pa - ang pagruruta ng kahilingan ay malapit na nauugnay sa pagpapatupad ng pattern ng MVC (Model View Controller). Kabilang dito ang paghihiwalay ng logic ng application, data at display.

Bun Framework MVC

Mahirap na makilala ang iyong sarili sa pagpapatupad ng MVC, kaya lahat ng bagay dito ay medyo prosaic din. Sa loob ng Core, inilalaan ko ang namespace ng Controller - dito lumikha ako ng isang base controller class na may access sa lahat ng bahagi ng system, nag-iimbak ng object ng application na naglunsad nito, at isang config service. Sa panahon ng pagsisimula, ang controller ay tumatanggap ng mga parameter ng paglulunsad: ang pangalan ng pamamaraan (aksyon) at ang mga argumento nito.

Inilalaan ko ang display code sa isang hiwalay na direktoryo ng View sa loob ng Core. Ang Bun Framework ay hindi nagbibigay ng anumang partikular na bahagi ng uri ng View - ipinapalagay na hinila mo lang ang nais na template sa loob ng controller, na nagpapasa ng data doon. Bilang default, nilalayon kong magdagdag ng suporta para sa Twig template engine at suporta para sa katutubong *.phtml na mga template sa framework

Ang huling bahagi ay ang Modelo. Naglalaan din ako ng hiwalay na namespace para dito sa loob ng Core: Model. Dito papasok ang isa pang pangunahing bahagi ng balangkas - ObjectMapper. Ang mga modelo mismo ay mga klase lamang, i.e. ay hindi ActiveRecord, ngunit nagpapatupad sila ng isang tiyak na Bun\Core\Model\ModelInterface. Ang mga klaseng ito ay maaaring gamitin ng ObjectMapper at i-save ang mga ito sa ilang uri ng imbakan.

Ngayon kailangan nating pag-usapan ang parehong object mapper at ang storage, magsimula tayo sa una.

Bun\Core\ObjectMapper

Ang object mapper ay marahil ang pinaka kumplikadong bahagi ng Core module ng framework. Ito ay isang serbisyo na maaaring gawing mga tala ang isang modelong object sa ilang database, at gawin din ang kabaligtaran - kumuha ng tala mula sa data store at i-map ito sa isang modelong object. Bilang default, ang Core module ay magsasama ng isang serbisyo na nagpapatupad ng pag-iimbak ng file ng mga bagay.

Bun\Core\Storage

Kinakatawan ng set ng Storage component ang abstraction at mga interface na dapat sundin ng anumang pagpapatupad ng storage sa isang application. Ang unang ganoong storage ay Bun\Core\Storage\FileStorage . Upang gumana, ang imbakan ng file ay gagamit ng isang hanay ng mga auxiliary na klase para sa pagtatrabaho sa mga file, pati na rin para sa pagbuo ng mga query upang maghanap ng mga tala sa imbakan ng file.

Ang object mapper na inilarawan sa itaas ay magagawang gumana sa anumang pagpapatupad ng Storage upang iimbak ang mga bagay nito doon. Narito ito ay nagkakahalaga ng pag-highlight ng isa pang mahalagang bahagi ng Core module - Repository.

Bun\Core\Repository

Ang mga repositoryo ay isang layer ng pag-access sa isang bagay. Upang hindi ma-overload ang object mapper na may functionality sa paghahanap at iba't ibang mga seleksyon ng mga bagay mula sa repository, inilipat ang gawaing ito sa repository. Ang imbakan ay maaaring gumana nang direkta sa imbakan, pumili ng data mula dito, at pagkatapos ay gawin itong mga bagay sa pamamagitan ng ObjectMapper at ilipat ito sa application.

Hayaan akong magbanggit kaagad ng isang bagay na may kaugnayan sa mga sangkap na inilarawan sa itaas - cache

Bun\Core\Cache

Cache – maglalaman ng isang hanay ng mga abstraction para sa pagpapatupad ng pakikipag-ugnayan sa iba't ibang mga imbakan ng cache, tulad ng Redis, Memacached, atbp. Bilang karagdagan, ang Core module ay isasama ang bahagi ng FileCacheDriver, na nagpapatupad ng data caching sa mga file.

Dito tayo nagpapatuloy sa isang mahalagang at, sa aking opinyon, bahagi ng balangkas na tumutukoy sa arkitektura. Ang kakayahang umangkop at pagpapalit ng mga bahagi ay nakakamit kapag ang iyong code ay hindi mahigpit na nakatali sa mga nangungunang serbisyo, ngunit maaaring mabilis na lumipat sa pagitan ng mga ito. Bilang karagdagan, ang susunod na bahagi ay gumagawa ng isang mahusay na trabaho ng mahusay na pag-aayos ng framework at application code.

Bun\Core\Lalagyan

Container – nagpapatupad ng isa sa mga paborito kong pattern ng programming – Dependency Injection. Ang dependency injection ay mabuti sa lahat - ang mga bahagi ng application ay mahinang umaasa sa mga partikular na bahagi, ang mga indibidwal na klase ay madaling subukan sa pamamagitan ng pagpapalit ng kanilang mga dependency. Maginhawang magpatupad ng mga alternatibong pagpapatupad sa ilang mga serbisyo, at pagkatapos ay madaling lumipat sa pagitan ng mga ito kahit na walang mga pagbabago sa code ng aplikasyon. Dagdag pa, ang iyong mga klase na gumagamit ng dependency injection ay malinaw na nagpapakita ng kanilang mga dependency - maaari ka ring bumuo ng dependency graph ng iyong mga bahagi ng application.

Sinisimulan at iniimbak ng container ang mga naka-configure na serbisyo sa panahon ng runtime ng application kapag ang mga serbisyong ito ay na-access ng application. Sa loob ng isang klase, mahalagang may kontrol ka sa lahat ng bahagi ng application.

Kinukumpleto nito ang mga pangunahing bahagi ng balangkas sa Core module sa ngayon. Sa panahon ng pagpapatupad, ang Core module ay maaaring magsama ng mga pagpapatupad ng pamamahala ng kaganapan, FormBuilder. Kabilang sa mga pantulong na bahagi, ito ay nagkakahalaga ng pagpuna sa pag-type ng mga pagbubukod. Ang base exception class na Bun\Core\Exception\Exception ay ibinigay upang ang lahat ng iba pang na-type na exception sa loob ng application at framework ay magmana mula rito. Nagbibigay ito ng sentralisadong exception interception sa antas ng aplikasyon at pinipigilan ang mga hindi nahuhuling pagbubukod na mangyari at maging sanhi ng pag-crash ng application.

Sa mga sumusunod na bahagi ng kuwento tungkol sa pagbuo ng Bun Framework, sisimulan kong pag-usapan ang bahagi ng Application, at ilarawan din ang kakanyahan ng mga autoloading file ayon sa pamantayan ng PSR0. Pagkatapos nito, magpapatuloy ako sa isang paglalarawan ng serbisyo sa pagsasaayos ng application at isang hanay ng mga bahagi ng Http

Sa mga pattern, nasiyahan ako sa mvc, registry. Para sa mga kahilingan, sumulat ako ng isang maliit na layer ng abstraction, para sa pagruruta - ang sarili kong function ng pag-parse ng kahilingan.
Ang istraktura ng web application ay magiging ganito

folder ng aplikasyon

Kasama sa input file na index.php ang bootstrap.php. Na, sa turn, ay nag-uugnay sa kernel, isang config file, ilang mga aklatan at nagsisimula sa router.

Gamitin ang Core\Route; require_once "lib/registry.php"; require_once "config.php"; require_once "lib/datebase.php"; require_once "core/model.php"; require_once "core/view.php"; require_once "core/controller.php"; require_once "core/route.php"; $router = bagong Ruta(); $router->start(); //simulan ang router

Ang pagpapatala ay simple:

Namespace Lib; class Lib_Registry ( static private $data = array(); static public function set($key, $value) ( ​​​​self::$data[$key] = $value; ) static public function get ($key) ( return isset( self::$data[$key]) ? self::$data[$key] : null ) static public function remove($key) ( if (isset(self::$data[$key])) ( unset (self::$data[$key]);

Narito ang mga getter at setter para sa pag-iimbak ng mga pandaigdigang halaga.

Gamitin ang Lib\Lib_Registry;

define("PATH_SITE", $_SERVER["DOCUMENT_ROOT"]);

define("HOST", "localhost");

define("USER", "root");
define("PASSWORD", "mypass");

/** * */ public function start() ( // catch AJAX request if ($this->getIsAjaxRequest()) ( ) session_start(); $this->dispatch(); ) /** * */ public function dispatch())( // nakatanggap ang dispatcher ng file na tumutugma sa pangalan ng controller, aksyon at mga argumento $this->getDirections($file, $controller, $action, $args); /* ******* ***** * isama ang Controller - Model */ if (is_readable($file) == false) ( die ("File $file 404 Not Found"); ) // isama ang controller kasama ($file = str_replace("controller" , "model", $file); // Dagdag na modelo kung(is_readable($model))( // ikonekta ang modelo kasama($model); ) /* ****** makuha ang klase ** */ $controller = ucfirst($controller); $class = ucfirst($this->namespace)."\Controller_" ); if (is_callable(array( $controller, $action)) == false) ( die ("Action $action 404 Not Found"); ) // tawagan ang action na $controller->$action($args)

Tinatawag ng dispatcher ang getDirections() na paraan, ibig sabihin. kumuha ng mga direktiba ng kahilingan. Bilang default, ang default na controller ay mga artikulo, ang aksyon ay index.

/** * @param $file * @param $controller * @param $action * @param $args */ private function getDirections(&$file, &$controller, &$action, &$args) ($route = ( walang laman($_SERVER["REQUEST_URI"])) ? "" : $_SERVER["REQUEST_URI"]; = $this->path; if (empty($route)) ( /* ******************* Default na direksyon ******** */ $ controller = "articles"; $action = "action_index"; $controller_path = $this->controller_path_folder = "application/controllers/$this->namespace/"; else ($parts = explode("/", $route); /* ************** namespace ********** */ if($parts = = "admin") ( $this->namespace = "admin"; array_shift($parts); ) /* ***************** folder at subfolder ***** * * */ $fullpath = $this->controller_path_folder = $controller_path ; $mga bahagi);

magpatuloy;

) if (is_file ($fullpath . ".php")) ( array_shift($parts); $file = "$fullpath.php"; break; ) ) /* ************* ** Controller, Action, Params ******** */ if(!isset($part)) $part = "articles";

$controller = $part;
if(!$file) $file = $fullpath."/$part.php";

$action = array_shift($parts);

if(!$action) $action = "action_index";

else $action = "action_$action";

$args = $parts;

Kapag nag-click kami sa isang link o programmatically sa loob ng isang Angular na application, ang kaukulang HTML5 API (Pushstate API) ay ginagamit, na nagbabago sa URL sa browser nang hindi nagpapadala ng HTTP na kahilingan. Ngunit kapag manu-mano naming ipinasok ang address ng mapagkukunan sa address bar ng browser, nagpapadala ang browser ng bagong kahilingan sa ASP.NET Core application.

Sa halimbawa sa itaas, ang kahilingang "produkto/1" ay ipinadala. Ngunit wala kaming controller na aksyon na nagmamapa sa naturang kahilingan. Kaya natural na magkakaroon tayo ng error. At kailangan naming magdagdag ng karagdagang server-side code upang ang mga naturang kahilingan ay maproseso din ng Angular application.

Ano ang maaari nating gawin sa kasong ito? Ang lahat ay nakasalalay sa kung paano nabuo ang web page. O ito ay direktang isang static na web page na direktang ipinadala sa user. O ito ay isang view (isang file na may cshtml extension) kung saan tinukoy ang loading code para sa Angulr application.

Mga query para sa mga static na file

Sa nakaraang paksa, ang Angular application ay na-load sa isang static na web page, index.html, na matatagpuan sa proyekto sa wwwroot folder:

Angular sa ASP.NET Core

At sa kasong ito, kailangan namin na kung ang kahilingan ay hindi inilaan para sa isang static na file, at hindi inilaan para sa alinman sa mga aksyon ng controller, ang index.html file ay ipapadala bilang tugon sa naturang kahilingan. At sa kasong ito, sa panig ng server maaari kaming gumamit ng ilang mga diskarte.

Patakbuhin ang paraan

Ang pinakamadaling paraan ay ang magdagdag ng middleware sa dulo ng pipeline ng pagpoproseso ng kahilingan, na magpapadala ng index.html file. Upang gawin ito, baguhin ang paraan ng Pag-configure sa klase ng Startup:

Pampublikong void Configure(IApplicationBuilder app, IHostingEnvironment env) ( if (env.IsDevelopment()) ( app.UseDeveloperExceptionPage(); app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions ( HotModuleReplacement = true )); ) app.UseFilesDefault(UseFiles); ); app.UseMvc(); // iproseso ang mga ruta na hindi naka-map sa mga mapagkukunan app.Run(async (context) => ( context.Response.ContentType = "text/html"; maghintay ng context.Response.SendFileAsync(Path .Combine(env.WebRootPath, "index.html"));

Kaya, para sa lahat ng mga kahilingan na hindi nakamapa sa mga mapagkukunan sa application, ang file na wwwroot/index.html ay ipapadala.

Ang kawalan ng diskarteng ito ay magiging mahirap para sa amin na paganahin ang 404 error handling sa panig ng server Dahil kung wala kaming mga controllers at mga aksyon na naaayon sa kahilingan, ang kahilingan ay mapoproseso pa rin, kahit na sa panig ng kliyente. sa Angular.

Pagpapadala ng file gamit ang controller method

Ang isang katulad na diskarte ay ibinibigay sa pamamagitan ng pagpapadala ng isang file gamit ang paraan ng controller. Halimbawa, sabihin nating mayroon kang sumusunod na HomeController sa iyong proyekto sa folder ng Controllers:

Gamit ang Microsoft.AspNetCore.Mvc; gamit ang System.IO; gamit ang Microsoft.AspNetCore.Hosting; namespace HelloAngularApp.Controllers ( public class HomeController: Controller ( IHostingEnvironment env; public HomeController(IHostingEnvironment env) ( this.env = env; ) public IActionResult Index() ( ibalik ang bagong PhysicalFileResult(Path.Combine(env.WebRootPath, "index.WebRootPath, "index. "), "text/html"); )))

Sa kasong ito, ipinapadala ng paraan ng Index ang index.html file.

Sa kasong ito, baguhin ang Configure method ng Startup class gaya ng sumusunod:

Pampublikong void Configure(IApplicationBuilder app, IHostingEnvironment env) ( if (env.IsDevelopment()) ( app.UseDeveloperExceptionPage(); app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions ( HotModuleReplacement = true )); ) `fault app.UseFilesStateDevMiddleware (); app.UseMvc(mga ruta =>

Sa kasong ito, dalawang ruta ang tinukoy para sa imprastraktura ng MVC. Ang unang ruta ay nagmamapa sa mga regular na controller at ang kanilang mga aksyon. Ang pangalawang ruta ay tinukoy gamit ang paraan ng MapSpaFallbackRoute() at mga mapa sa pagkilos ng Index ng Home controller, na sa kasong ito ay nagpapadala ng index.html file pabalik sa user.

Mga kahilingan sa mga pagkilos ng controller

Maaari naming opsyonal na tukuyin ang paglo-load ng isang Angular na application sa isang static na html na pahina. Maaari rin itong isang pagganap. Halimbawa, tukuyin natin ang direktoryo ng Views/Home sa proyekto at maglagay ng bagong file na Index.cshtml dito:

Angular sa ASP.NET Core

Sa kasong ito, ang view ay naglalaman lamang ng html code, bagaman kung kinakailangan, maaari mo ring tukuyin ang mga tagubilin sa Razor dito, lumikha ng isang master page para dito, atbp.

At ang paraan ng HomeController's Index ay gagamit ng view na ito:

Gamit ang Microsoft.AspNetCore.Mvc; namespace HelloAngularApp.Controllers ( public class HomeController: Controller ( public IActionResult Index() ( return View(); ) ) )

Sa Configure method ng Startup class, tinukoy namin ang sumusunod na code:

Pampublikong void Configure(IApplicationBuilder app, IHostingEnvironment env) ( if (env.IsDevelopment()) ( app.UseDeveloperExceptionPage(); app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions ( HotModuleReplacement = true )); ) //app.UseDefa ang paraan ay hindi na kailangan app.UseStaticFiles(); app.UseMvc(routes => ( routes.MapRoute(name: "default", template: "(controller=Home)/(action=Index)/(id?)" ); routes.MapSpaFallbackRoute("angular-fallback", bago ( controller = "Home", action = "Index"));

Dito muli, gamit ang mga ruta.MapSpaFallbackRoute na pamamaraan, ang lahat ng iba pang mga kahilingan na hindi nakamapa sa iba pang mga mapagkukunan ay hahawakan ng paraan ng Index ng HomeController.