1: <?php
2:
3: namespace Zippy;
4:
5: use Zippy\Html\HtmlContainer;
6:
7: /**
8: * Класс приложения. Обеспечивает жизненный цикл страниц, управление сессией
9: * и обработку HTTP запросов.
10: * Для использования необходимо отнаследоватся и переопределить, как минимум,
11: * getTemplate().
12: *
13: */
14: abstract class WebApplication
15: {
16: private $currentpage = null;
17: public static $app = null;
18: public static $dom = null;
19:
20: private $reloadPage = false;
21: private $request;
22: private $response;
23:
24:
25: // public $doc;
26:
27: /**
28: * Конструктор
29: * @param string Имя класса начальной страницы
30: */
31: public function __construct() {
32:
33:
34: self::$app = $this;
35:
36:
37: $this->request = new HttpRequest();
38: $this->response = new HttpResponse();
39: }
40:
41: public function __sleep() {
42: //avoid serialization
43: }
44:
45: /**
46: * Возвращает экземпляр приложения
47: * @return WebApplication
48: */
49: final public static function getApplication() {
50: return self::$app;
51: }
52:
53: /**
54: * Возвращает HTML шаблон по имени класса страницы
55: * перегружается в классе пользовательского приложения
56: * @param string Имя класса страницы
57: */
58: abstract public function getTemplate($name);
59:
60:
61: /**
62: * Возвращает объект текущей страницы
63: * @return WebPage
64: */
65: final public function getCurrentPage() {
66: return $this->currentpage;
67: }
68:
69: /**
70: * Создает новый экземпляр страницы по имени класса страницы
71: * и делает ее текущей. По сути серверный редирект без изменения адресной строки браузера.
72: * @param string Имя класса страницы
73: */
74: final public function LoadPage($name, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null, $arg5 = null) {
75:
76: if (is_array($arg1) == false) {
77: //$this->currentpage = new $name($arg1, $arg2, $arg3, $arg4, $arg5);
78: $arg1 = func_get_args();
79: $arg1 = array_slice($arg1, 1);
80: }
81:
82: $classpage = new \ReflectionClass($name);
83: $this->currentpage = $classpage->newInstanceArgs($arg1);
84:
85:
86: $this->currentpage->args = $arg1; //запоминаем аргументы страницы
87:
88: $this->response->setPageIndex($this->getPageManager()->updatePage($this->currentpage));
89: }
90:
91: /**
92: * Основной цикл приложения
93: */
94: final public function Run($homepage) {
95: self::$app = $this;
96:
97: if ($homepage == null) {
98: throw new \Zippy\Exception(ERROR_NOT_FOUND_HOMEPAGE);
99: }
100:
101: if ($this->request->querytype == HttpRequest::QUERY_INVALID) {
102: //self::Redirect404();
103: self::$app->getResponse()->toIndexPage();
104: self::$app->getResponse()->output();
105: die;
106: }
107:
108: if ($this->request->querytype == HttpRequest::QUERY_HOME) {
109: $this->LoadPage($homepage);
110: }
111:
112: if ($this->request->querytype == HttpRequest::QUERY_PAGE) {
113: $this->LoadPage($this->request->request_page, $this->request->request_page_arg);
114: }
115:
116: if ($this->request->querytype == HttpRequest::QUERY_SEF) {
117: $this->currentpage = null;
118:
119: $this->Route($this->request->uri);
120:
121:
122: }
123:
124: if ($this->request->querytype == HttpRequest::QUERY_EVENT) {
125: //получаем адресуемую страницу
126: if (strlen($this->request->getRequestIndex())==0) {
127: $this->response->to404Page();
128: }
129:
130: $this->currentpage = $this->getPageManager()->getPage($this->request->getRequestIndex());
131: if ($this->currentpage == null) {
132: if (strlen($this->request->request_page) > 2) {
133: $this->LoadPage($this->request->request_page);
134: } else {
135: $this->currentpage = $this->getPageManager()->getLastPage();
136: }
137: }
138:
139: $this->response->setPageIndex($this->getRequest()->getRequestIndex());
140:
141: if ($this->currentpage instanceof \Zippy\Html\WebPage == false) {
142: $this->response->toIndexPage();
143: $this->response->output();
144: return;
145: }
146: if ($this->request->querytype == HttpRequest::QUERY_EVENT) {
147: $this->currentpage->RequestHandle();
148: }
149:
150:
151:
152: if ($this->request->isAjaxRequest() || $this->request->isBinaryRequest()) {
153: // если Ajax запрос, отдаем в выходной поток только ответ
154: // адресуемого елемента
155: // $this->output = $this->currentpage->getAjaxAnswer();
156:
157: $this->getPageManager()->updatePage($this->currentpage);
158: } else {
159:
160: $this->response->setPageIndex($this->getPageManager()->updatePage($this->currentpage));
161: if ($_SERVER["REQUEST_METHOD"] == "GET") {
162: if ($this->reloadPage == true) { //если надо сбросить адресную строку
163: $this->response->toBaseUrl();
164: }
165: }
166: if ($_SERVER["REQUEST_METHOD"] == "POST") {
167:
168: // $this->response->toBaseUrl();
169: }
170: }
171: }
172:
173: if ($this->currentpage == null) {
174: $this->response->to404Page();
175: }
176:
177: if ($this->response->isRedirect() != true) {
178: $this->Render();
179: }
180:
181: $this->response->output();
182: }
183:
184: /**
185: * Метод, выполняющий формирование выходного HTML потока
186: * на основе текущено шаблона и данных елементов страницы
187: */
188: private function Render() {
189: if ($this->request->isBinaryRequest()) {
190: return;
191: }
192:
193: $renderpage = $this->currentpage;
194:
195: if ($this->request->isAjaxRequest() && !$this->currentpage->hasAB()) {
196: // если нет ajax blocks
197: return;
198: }
199:
200: //загружаем соответсвующий шаблон
201: $template = $this->getTemplate(get_class($renderpage));
202:
203:
204: self::$dom = (new \DOMWrap\Document())->html($template);
205:
206:
207: $basepage = get_parent_class($renderpage);
208: // если страница не базовая а дочерняя
209: if ($basepage !== "Zippy\\Html\\WebPage") {
210:
211: $basetemplate = WebApplication::getApplication()->getTemplate($basepage);
212:
213:
214: $body = self::$dom->find('body')->html();
215:
216:
217: $basetemplate= str_replace('<childpage/>', $body, $basetemplate) ;
218:
219:
220: $bdom = (new \DOMWrap\Document())->html($basetemplate);
221:
222: $bhead=$bdom->find('head');
223:
224: $links = self::$dom->find('head > link') ;
225: foreach ($links as $l) {
226: $bhead->appendWith($l);
227: }
228: $scripts = self::$dom->find('head > script') ;
229: foreach ($scripts as $s) {
230: $bhead->appendWith($s);
231: }
232: $metas = self::$dom->find('head > meta') ;
233: foreach ($metas as $m) {
234: $bhead->appendWith($m);
235: }
236: $title = self::$dom->find('head > title') ;
237: foreach ($title as $t) {
238: $bhead->find('title')->destroy();
239: $bhead->prependWith($t);
240: }
241: self::$dom = $bdom;
242: }
243:
244: $renderpage->Render();
245:
246: $head=self::$dom->find('head');
247: //тэги должны уже быть
248: if (strlen($renderpage->_title) > 0) {
249: $head->find('title')->text($renderpage->_title);
250: }
251: if (strlen($renderpage->_keywords) > 0) {
252: $head->find('meta[name="keywords"]')->attr('content', $renderpage->_keywords);;
253: }
254: if (strlen($renderpage->_description) > 0) {
255: $head->find('meta[name="description"]')->attr('content', $renderpage->_description);;
256: }
257:
258: $response= self::$dom->html();
259: // file_put_contents('z:/r2.html',$response) ;
260:
261: if (count($renderpage->_tvars) > 0) {
262:
263: //восстанавливаем скобки Mustache в тегах
264: $response = str_replace("%7B%7B", "{{", $response);
265: $response = str_replace("%7D%7D", "}}", $response);
266:
267: $m = new \Mustache_Engine();
268: $response = $m->render($response, $renderpage->_tvars);
269: }
270:
271: if ($this->request->isAjaxRequest()) {
272: // если ajax blocks
273:
274: // \phpQuery::newDocumentHTML($response);
275: $renderpage->updateAjaxHTML() ;
276:
277: return;
278:
279: }
280:
281: $this->response->setContent($response);
282: }
283:
284:
285: /**
286: * Метод выполняющий роутинг URL запросов. Например для ЧПУ ссылок
287: * Переопределяется в пользовательском приложении.
288: * Загружает страницу (с параметрами) методом $this->LoadPage
289: * @param string Входящий запрос
290: */
291: protected function Route($uri) {
292:
293: }
294:
295: /**
296: * Указывает что текущая страница должна перегрузиться с помощью
297: * клиентского редиректа для сброса адресной строки
298: * @param boolean
299: */
300: final public function setReloadPage($value = true) {
301: $this->reloadPage = $value;
302: }
303:
304: /**
305: * Возвращает объект HttpRequest
306: * @return HttpRequest
307: */
308: final public function getRequest() {
309: return $this->request;
310: }
311:
312: /**
313: * возвращает объект Httpresponse
314: * @return HttpResponse
315: */
316: final public function getResponse() {
317: return $this->response;
318: }
319:
320: /**
321: * Возвращает менеджер страниц
322: * @return PageManager
323: */
324: protected function getPageManager() {
325: if (!isset($_SESSION['zippy_pagemanager'])) {
326: $_SESSION['zippy_pagemanager'] = new PageManager();
327: }
328: return $_SESSION['zippy_pagemanager'];
329: }
330:
331:
332: /**
333: * возвращает имя класса предыдущей страницы
334: *
335: */
336: public function getPrevPage() {
337: return $this->getPageManager()->getPrevPage();
338: }
339:
340: /**
341: * Редирект на страницу 404
342: *
343: */
344: public static function Redirect404() {
345: self::$app->getResponse()->to404Page();
346: }
347:
348: /**
349: * Редирект на предыдущую страницу
350: *
351: */
352: public static function RedirectBack() {
353: $pagename = self::$app->getPageManager()->getPrevPage();
354: $pagename = '\\' . rtrim($pagename, '\\');
355:
356: if ($pagename == "\\") {
357: self::$app->response->toIndexPage();
358: return;
359: }
360: self::$app->response->Redirect($pagename, array());
361: }
362:
363: /**
364: * Вызывается перед обработкой запроса
365: * Перегружается в приложении
366: * если есть префикс (например указан язык)
367: * возвращает массив с элементами:
368: * префикс
369: * uri без префикса
370: * @param mixed $uri
371: */
372: public function beforeRequest($uri) {
373: return null;
374: }
375:
376: /**
377: * Выполняет клиентский редирект на страницу
378: *
379: * @param mixed $page Имя класса страницы
380: * @param mixed $arg1
381: * @param mixed $arg2
382: * @param mixed $arg3
383: * @param mixed $arg4
384: * @param mixed $arg5
385: */
386: public static function Redirect($page, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null, $arg5 = null) {
387: if (self::$app instanceof WebApplication) {
388: self::$app->getResponse()->Redirect($page, $arg1, $arg2, $arg3, $arg4, $arg5);
389: }
390: }
391:
392: /**
393: * Редирект на домашнюю страницу
394: *
395: */
396: public static function RedirectHome() {
397: self::$app->getResponse()->toIndexPage();
398: }
399:
400:
401: /**
402: * Вставляет JavaScript в конец выходного потока
403: * @param string Код скрипта
404: * @param boolean Если true - вставка после загрузки документа в браузер
405: */
406: public static function addJavaScript($js, $docready = false) {
407: return self::$app->getResponse()->addJavaScript($js, $docready);
408: }
409:
410: /**
411: * редирект по URL
412: *
413: * @param mixed $message
414: * @param mixed $uri
415: */
416: public static function RedirectURI($uri) {
417: self::$app->getResponse()->toPage($uri);
418: }
419:
420: }
421: