├── .gitignore ├── bitrix └── main │ ├── components │ ├── Breadcrumbs │ │ ├── Controller.php │ │ └── templates │ │ │ └── default │ │ │ ├── resources │ │ │ └── css │ │ │ │ └── style.css │ │ │ └── template.twig │ ├── DemoForum │ │ ├── Controller.php │ │ └── templates │ │ │ └── default │ │ │ └── template.twig │ ├── DemoForumSection │ │ ├── Controller.php │ │ └── templates │ │ │ └── default │ │ │ └── template.twig │ ├── DemoForumThread │ │ ├── Controller.php │ │ └── templates │ │ │ └── default │ │ │ ├── message.twig │ │ │ └── template.twig │ ├── DemoLastNews │ │ ├── Controller.php │ │ └── templates │ │ │ └── default │ │ │ └── template.twig │ ├── DemoNewsArticle │ │ ├── Controller.php │ │ ├── config │ │ │ └── routes │ │ │ │ └── web.php │ │ └── templates │ │ │ └── default │ │ │ ├── comments.twig │ │ │ └── detail.twig │ ├── DemoNewsList │ │ ├── Controller.php │ │ ├── config │ │ │ └── routes │ │ │ │ └── web.php │ │ └── templates │ │ │ └── default │ │ │ └── template.twig │ ├── DemoUser │ │ ├── Controller.php │ │ ├── config │ │ │ └── routes │ │ │ │ └── web.php │ │ └── templates │ │ │ └── default │ │ │ └── template.twig │ ├── DemoUsers │ │ ├── Controller.php │ │ ├── config │ │ │ └── routes │ │ │ │ └── web.php │ │ └── templates │ │ │ └── default │ │ │ └── template.twig │ ├── ErrorPages │ │ ├── ErrorPagesController.php │ │ └── templates │ │ │ └── default │ │ │ ├── 403.twig │ │ │ ├── 404.twig │ │ │ └── 503.twig │ ├── JsCore │ │ ├── Configuration.php │ │ ├── Extension.php │ │ └── resources │ │ │ └── js │ │ │ └── core.js │ ├── MaintenanceMode │ │ ├── MaintenanceModeController.php │ │ ├── MaintenanceModeMiddleware.php │ │ └── templates │ │ │ └── default │ │ │ └── onMaintenance.twig │ ├── Menu │ │ ├── Controller.php │ │ └── templates │ │ │ └── default │ │ │ ├── resources │ │ │ └── css │ │ │ │ └── style.css │ │ │ └── template.twig │ └── UserProfile │ │ ├── Controller.php │ │ └── config │ │ └── routes │ │ └── web.php │ ├── config │ └── test.php │ ├── layouts │ └── LightV3 │ │ ├── LightV3Layout.php │ │ ├── footer.twig │ │ ├── header.twig │ │ ├── layout.twig │ │ └── resources │ │ ├── images │ │ ├── bg.jpg │ │ ├── delivery.svg │ │ ├── editor_sprite.png │ │ ├── foooter_bg.png │ │ ├── footer_h4_bg.png │ │ ├── header_1.png │ │ ├── label.svg │ │ ├── lightblue_bg.png │ │ ├── logo.png │ │ ├── logo_mobile.png │ │ ├── logo_mobile_retina.png │ │ ├── logo_retina.png │ │ ├── phone.svg │ │ ├── phone_white.svg │ │ ├── ru │ │ │ ├── preview_horizontal.gif │ │ │ ├── preview_vertical.gif │ │ │ ├── screen_horizontal.gif │ │ │ └── screen_vertical.gif │ │ ├── sale.svg │ │ ├── self.svg │ │ ├── wt.png │ │ └── x2footer_h4_bg.png │ │ └── template_styles.css │ ├── lib │ ├── AssetManager.php │ ├── Cache │ │ ├── Adapters │ │ │ ├── AbstractAdapter.php │ │ │ ├── FilesystemAdapter.php │ │ │ ├── LockableAdapterInterface.php │ │ │ └── MemcachedAdapter.php │ │ ├── Cache.php │ │ ├── EmptyResult.php │ │ ├── Item.php │ │ ├── LockPolicy.php │ │ └── Pool.php │ ├── Configuration │ │ ├── Configuration.php │ │ ├── Controller │ │ │ ├── Attributes │ │ │ │ ├── ComponentAlias.php │ │ │ │ └── RouteName.php │ │ │ ├── ConfigurationItem.php │ │ │ └── ControllerConfiguration.php │ │ └── RewriteValue.php │ ├── Context.php │ ├── Controllers │ │ ├── Controller.php │ │ ├── ControllerFactory.php │ │ └── Extension.php │ ├── Event.php │ ├── EventManager.php │ ├── EventResult.php │ ├── Exceptions │ │ ├── ArgumentException.php │ │ ├── ErrorResponseException.php │ │ └── SystemException.php │ ├── ExtensionLoader.php │ ├── Kernel.php │ ├── Layout.php │ ├── Middleware │ │ └── Middleware.php │ ├── Model │ │ └── NewsModel.php │ ├── Module.php │ ├── PackageLeadClassTrait.php │ ├── QueueRequestHandler.php │ ├── ResourceMeta.php │ ├── Response.php │ ├── Routing │ │ ├── CompileCache.php │ │ ├── Controllers │ │ │ ├── ClosureController.php │ │ │ ├── Controller.php │ │ │ ├── EngineActionController.php │ │ │ ├── EngineController.php │ │ │ ├── FallbackController.php │ │ │ ├── PublicPageController.php │ │ │ ├── RouteControllerFactory.php │ │ │ └── TwigPageController.php │ │ ├── Exceptions │ │ │ └── ParameterNotFoundException.php │ │ ├── Options.php │ │ ├── RoutablePackageTrait.php │ │ ├── Route.php │ │ ├── RouteMatchMiddleware.php │ │ ├── RouteRequestHandler.php │ │ ├── Router.php │ │ ├── RoutingConfiguration.php │ │ ├── RoutingConfigurator.php │ │ └── RoutingRequestHandler.php │ ├── Site.php │ ├── Solution.php │ ├── StaticController.php │ ├── Templates │ │ ├── Template.php │ │ └── TemplateRouter.php │ ├── Twig │ │ ├── Bitrix │ │ │ ├── Extension │ │ │ │ └── BitrixExtension.php │ │ │ ├── Node │ │ │ │ └── LayoutExpression.php │ │ │ ├── NodeVisitor │ │ │ │ └── ExtendsNodeVisitor.php │ │ │ └── TokenParser │ │ │ │ └── LayoutParser.php │ │ ├── FileCache.php │ │ └── Stack │ │ │ ├── Extension │ │ │ └── StackExtension.php │ │ │ ├── Node │ │ │ ├── BufferedBodyNode.php │ │ │ ├── BufferedConcatNode.php │ │ │ ├── BufferedNode.php │ │ │ ├── BufferedPrintNode.php │ │ │ ├── BufferedTextNode.php │ │ │ ├── DeferredBodyNode.php │ │ │ ├── DeferredNode.php │ │ │ ├── PushBodyNode.php │ │ │ └── PushNode.php │ │ │ ├── NodeVisitor │ │ │ ├── BufferedBodyNodeVisitor.php │ │ │ ├── BufferedOutputNodeVisitor.php │ │ │ └── StackNodeVisitor.php │ │ │ ├── Stack.php │ │ │ ├── StackPointer.php │ │ │ └── TokenParser │ │ │ └── PushTokenParser.php │ └── Type │ │ ├── ArrayHelper.php │ │ ├── Dictionary.php │ │ └── ParameterDictionary.php │ ├── meta │ └── resource.map.php │ ├── sites │ └── Supershop │ │ ├── SupershopSite.php │ │ ├── config │ │ └── routes │ │ │ └── web.php │ │ ├── pages │ │ ├── about.twig │ │ ├── company.twig │ │ ├── index.twig │ │ └── sales.twig │ │ └── routes │ │ └── routes.php │ └── solutions │ ├── Forum │ ├── ForumSolution.php │ └── config │ │ └── routes │ │ └── web.php │ └── Users │ ├── UsersSolution.php │ └── config │ └── routes │ └── web.php ├── composer.json ├── config ├── app.php ├── bootstrap.php ├── cache.php ├── container │ └── base.php ├── controllers │ └── forum.php ├── db.php ├── maintenance.php ├── middleware.php └── routes │ └── web.php ├── public ├── favicon.ico └── index.php └── sites └── s3 ├── Site.php ├── config └── routes │ └── web.php └── pages ├── 404.twig ├── about.twig ├── blank.twig └── index.twig /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/* 2 | cache/* 3 | public/resources/* -------------------------------------------------------------------------------- /bitrix/main/components/Breadcrumbs/Controller.php: -------------------------------------------------------------------------------- 1 | 'Point 1', 23 | 'LINK' => '/point/1' 24 | ], 25 | [ 26 | 'TITLE' => 'Point 2', 27 | 'LINK' => '/point/2' 28 | ], 29 | [ 30 | 'TITLE' => 'Point 3', 31 | 'LINK' => '/point/3' 32 | ], 33 | ]; 34 | 35 | $jsonLd = $this->getJsonLd($crumbs); 36 | 37 | return $this->render('default/template', compact('crumbs', 'jsonLd')); 38 | } 39 | 40 | protected function getJsonLd($crumbs) 41 | { 42 | $jsonLDBreadcrumbList = ''; 43 | $itemSize = count($crumbs); 44 | 45 | for($index = 0; $index < $itemSize; $index++) 46 | { 47 | $title = htmlspecialchars($crumbs[$index]["TITLE"]); 48 | $jsonSeparator = ($index > 0? ',' : ''); 49 | 50 | if($crumbs[$index]["LINK"] <> "" && $index != $itemSize-1) 51 | { 52 | $jsonLDBreadcrumbList .= $jsonSeparator.'{ 53 | "@type": "ListItem", 54 | "position": '.$index.', 55 | "name": "'.$title.'", 56 | "item": "'.$crumbs[$index]["LINK"].'" 57 | }'; 58 | } 59 | } 60 | 61 | return ' 62 | 69 | '; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /bitrix/main/components/Breadcrumbs/templates/default/resources/css/style.css: -------------------------------------------------------------------------------- 1 | .store-breadcrumb { 2 | display: block; 3 | } 4 | 5 | .store-breadcrumb-item { 6 | width: 33px; 7 | height: 22px; 8 | display: none; 9 | } 10 | 11 | .store-breadcrumb .store-breadcrumb-item:last-child {display: block;} 12 | 13 | .store-breadcrumb-item-link { 14 | border-bottom: none; 15 | text-decoration: none; 16 | opacity: .3; 17 | display: flex; 18 | align-items: center; 19 | justify-content: flex-start; 20 | width: 33px; 21 | height: 22px; 22 | transition: 170ms linear all; 23 | } 24 | 25 | .store-breadcrumb-item-link:hover { 26 | border-bottom: none; 27 | text-decoration: none; 28 | opacity: 1; 29 | } 30 | -------------------------------------------------------------------------------- /bitrix/main/components/Breadcrumbs/templates/default/template.twig: -------------------------------------------------------------------------------- 1 |
2 | 3 | {% for crumb in crumbs %} 4 | {% if not loop.last and crumb.LINK is not empty %} 5 |
6 | 7 | 8 | 9 | 10 | 11 |
12 | {% endif %} 13 | {% endfor %} 14 | 15 |
16 | 17 | {{ jsonLd|raw }} -------------------------------------------------------------------------------- /bitrix/main/components/DemoForum/Controller.php: -------------------------------------------------------------------------------- 1 | render('default/template', compact('sections')); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoForum/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | forum main page 2 | 3 |

4 | 5 | sections: 6 | 7 |

8 | 9 | {#
  • section 1
  • #} 10 | {#
  • section 2
  • #} 11 | 12 |
  • section 1
  • 13 |
  • section 2
  • -------------------------------------------------------------------------------- /bitrix/main/components/DemoForumSection/Controller.php: -------------------------------------------------------------------------------- 1 | prepareThreads(); 20 | return $this->render('default/template', compact('threads')); 21 | } 22 | 23 | protected function prepareThreads() 24 | { 25 | return [1]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoForumSection/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | section page 2 | 3 |

    4 | 5 | threads: 6 | 7 |

    8 | 9 | {#
  • thread 1
  • #} 10 | {#
  • thread 2
  • #} 11 | 12 |
  • thread 1
  • 13 |
  • thread 2
  • 14 | 15 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoForumThread/Controller.php: -------------------------------------------------------------------------------- 1 | render('default/template', [ 22 | 'perPage' => $this->perPage 23 | ]); 24 | } 25 | 26 | public function messageAction() 27 | { 28 | $sections = []; 29 | return $this->render('default/message', compact('sections')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoForumThread/templates/default/message.twig: -------------------------------------------------------------------------------- 1 | single message -------------------------------------------------------------------------------- /bitrix/main/components/DemoForumThread/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | thread page 2 | 3 |

    4 | 5 | messages: 6 | 7 |

    8 | 9 |
  • 10 | {# message 1#} 11 | {# by username1#} 12 | 13 | {# by username1#} 14 | {# from thread#} 15 | 16 | by username1 ({{ this.router.linkTo('users_profile', {'id':5}) }}) 17 | from thread 18 |
  • -------------------------------------------------------------------------------- /bitrix/main/components/DemoLastNews/Controller.php: -------------------------------------------------------------------------------- 1 | render('default/template', compact('news')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoLastNews/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | news list page 2 |
    3 | {% for article in news %} 4 |
  • 5 | {# {{ article.title }}#} 6 | {{ article.title }} 7 |
  • 8 | {% endfor %} -------------------------------------------------------------------------------- /bitrix/main/components/DemoNewsArticle/Controller.php: -------------------------------------------------------------------------------- 1 | getParameterValue('id'); 23 | $article = NewsModel::getById($id); 24 | 25 | return $this->render('default/detail', compact('article')); 26 | } 27 | 28 | public function commentsAction() 29 | { 30 | return $this->render('default/comments', []); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoNewsArticle/config/routes/web.php: -------------------------------------------------------------------------------- 1 | get('{id}', [ 8 | \Bitrix\Main\Components\DemoNewsArticle\Controller::class, 9 | 'detailAction', 10 | ])->name('detail'); 11 | 12 | $routes->get('{id}/comments', [ 13 | \Bitrix\Main\Components\DemoNewsArticle\Controller::class, 14 | 'commentsAction', 15 | ])->name('comments'); 16 | }; -------------------------------------------------------------------------------- /bitrix/main/components/DemoNewsArticle/templates/default/comments.twig: -------------------------------------------------------------------------------- 1 | news comments page -------------------------------------------------------------------------------- /bitrix/main/components/DemoNewsArticle/templates/default/detail.twig: -------------------------------------------------------------------------------- 1 | news detail page 2 | 3 |

    4 |

    {{ article.title }}

    -------------------------------------------------------------------------------- /bitrix/main/components/DemoNewsList/Controller.php: -------------------------------------------------------------------------------- 1 | render('default/template', compact('news')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoNewsList/config/routes/web.php: -------------------------------------------------------------------------------- 1 | get('/', [ 8 | \Bitrix\Main\Components\DemoNewsList\Controller::class, 9 | 'listAction', 10 | ])->name('list'); 11 | 12 | $routes 13 | ->name('article') 14 | ->component(\Bitrix\Main\Components\DemoNewsArticle\Controller::class); 15 | 16 | }; -------------------------------------------------------------------------------- /bitrix/main/components/DemoNewsList/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | news list page 2 |
    3 | {% for article in news %} 4 |
  • 5 | {# {{ article.title }}#} 6 | {{ article.title }} 7 |
  • 8 | {% endfor %} -------------------------------------------------------------------------------- /bitrix/main/components/DemoUser/Controller.php: -------------------------------------------------------------------------------- 1 | render('default/template', compact('sections')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoUser/config/routes/web.php: -------------------------------------------------------------------------------- 1 | get('{id}', [ 8 | \Bitrix\Main\Components\DemoUser\Controller::class, 9 | 'mainAction', 10 | ])->name('profile'); 11 | }; -------------------------------------------------------------------------------- /bitrix/main/components/DemoUser/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | user profile -------------------------------------------------------------------------------- /bitrix/main/components/DemoUsers/Controller.php: -------------------------------------------------------------------------------- 1 | render('default/template', compact('sections')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /bitrix/main/components/DemoUsers/config/routes/web.php: -------------------------------------------------------------------------------- 1 | get('/', [ 8 | \Bitrix\Main\Components\DemoUsers\Controller::class, 9 | 'listAction', 10 | ])->name('list'); 11 | 12 | $routes 13 | ->name('user') 14 | ->component(\Bitrix\Main\Components\DemoUser\Controller::class); 15 | }; -------------------------------------------------------------------------------- /bitrix/main/components/DemoUsers/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | users: 2 | 3 |

    4 | 5 |
  • user 1
  • 6 |
  • user 2
  • -------------------------------------------------------------------------------- /bitrix/main/components/ErrorPages/ErrorPagesController.php: -------------------------------------------------------------------------------- 1 | render('default/403'); 14 | } 15 | 16 | public function show404Error() 17 | { 18 | return $this->render('default/404'); 19 | } 20 | 21 | public function show503Error() 22 | { 23 | return $this->render('default/503'); 24 | } 25 | } -------------------------------------------------------------------------------- /bitrix/main/components/ErrorPages/templates/default/403.twig: -------------------------------------------------------------------------------- 1 | bitrix 403 -------------------------------------------------------------------------------- /bitrix/main/components/ErrorPages/templates/default/404.twig: -------------------------------------------------------------------------------- 1 | bitrix 404 -------------------------------------------------------------------------------- /bitrix/main/components/ErrorPages/templates/default/503.twig: -------------------------------------------------------------------------------- 1 | bitrix 503 -------------------------------------------------------------------------------- /bitrix/main/components/JsCore/Configuration.php: -------------------------------------------------------------------------------- 1 | render('default/onMaintenance'); 12 | 13 | return new Response(status: 503, body: $responseText); // TODO response factory 14 | } 15 | } -------------------------------------------------------------------------------- /bitrix/main/components/MaintenanceMode/MaintenanceModeMiddleware.php: -------------------------------------------------------------------------------- 1 | configuration = $configuration; 21 | $this->controllerFactory = $controllerFactory; 22 | } 23 | 24 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 25 | { 26 | if ($this->configuration->get('maintenance.mode')) 27 | { 28 | // call controller of this component 29 | $controller = $this->controllerFactory->create(MaintenanceModeController::class); 30 | 31 | return $controller->showMaintenanceMessage(); 32 | } 33 | else 34 | { 35 | $response = $handler->handle($request); 36 | } 37 | 38 | return $response; 39 | } 40 | } -------------------------------------------------------------------------------- /bitrix/main/components/MaintenanceMode/templates/default/onMaintenance.twig: -------------------------------------------------------------------------------- 1 | please wait -------------------------------------------------------------------------------- /bitrix/main/components/Menu/Controller.php: -------------------------------------------------------------------------------- 1 | [ 22 | [ 23 | 'DEPTH_LEVEL' => 1, 24 | 'SELECTED' => false, 25 | 'LINK' => '/something/1', 26 | 'TEXT' => 'Одежда', 27 | ], 28 | [ 29 | 'DEPTH_LEVEL' => 1, 30 | 'SELECTED' => false, 31 | 'LINK' => '/something/2', 32 | 'TEXT' => 'Обувь', 33 | ], 34 | [ 35 | 'DEPTH_LEVEL' => 1, 36 | 'SELECTED' => false, 37 | 'LINK' => '/something/3', 38 | 'TEXT' => 'Спортивный инвентарь', 39 | ] 40 | ], 41 | 'parameters' => [ 42 | 'MAX_LEVEL' => 1 43 | ], 44 | ]; 45 | 46 | return $this->render('default/template', $data); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bitrix/main/components/Menu/templates/default/resources/css/style.css: -------------------------------------------------------------------------------- 1 | .main-menu { 2 | display: block; 3 | padding-left: 16px; 4 | padding-top: 5px; 5 | margin: 5px 0 35px; 6 | width: 100%; 7 | list-style: none; 8 | } 9 | 10 | .menu-main-items-container { 11 | box-sizing: border-box; 12 | /*background-color: #f3f3f3;*/ 13 | transition: width 170ms ease; 14 | max-width: 275px; 15 | } 16 | 17 | header .menu-main-items-container { background-color: #f3f3f3; } 18 | 19 | .menu-main-items { 20 | display: block; 21 | padding: 12px 0 0; 22 | margin: 0; 23 | } 24 | 25 | .menu-main-item { 26 | display: block; 27 | padding: 0 0 0 17px; 28 | margin: 0; 29 | } 30 | 31 | .menu-main-item-link { 32 | padding: 0 17px 0 0; 33 | width: 100%; 34 | display: flex; 35 | align-items: center; 36 | justify-content: space-between; 37 | position: relative; 38 | min-height: 40px; 39 | text-decoration: none !important; 40 | vertical-align: middle; 41 | } 42 | 43 | .menu-main-item-text { 44 | font-style: normal; 45 | font-weight: normal; 46 | font-size: 16px; 47 | line-height: 21px; 48 | color: #121212; 49 | opacity: 0.6; 50 | } 51 | 52 | .menu-main-item-angle { 53 | display: block; 54 | transform: rotate(-45deg); 55 | border-bottom: 1px solid; 56 | border-right: 1px solid; 57 | color: #121212; 58 | opacity: 0.6; 59 | width: 7px; 60 | height: 7px; 61 | } -------------------------------------------------------------------------------- /bitrix/main/components/Menu/templates/default/template.twig: -------------------------------------------------------------------------------- 1 | 23 | 24 | {% if get_slot('extraNavigation') is empty %} 25 | {% push extraNavigation %} 26 | extra navigation 27 | {% endpush %} 28 | {% endif %} -------------------------------------------------------------------------------- /bitrix/main/components/UserProfile/Controller.php: -------------------------------------------------------------------------------- 1 | get('', function () { 8 | // echo 'nothing '; 9 | // })->name('nothing'); 10 | 11 | $routes->get('{id}', function () { 12 | echo 'profile ' . \Bitrix\Main\Lib\Context::getRoute()->getParameterValue('id'); 13 | var_dump(\Bitrix\Main\Lib\Context::getRoute()->getParametersValues()); 14 | })->name('profile'); 15 | 16 | $routes->get('{id}/calendar', function () { 17 | echo 'calendar ' . \Bitrix\Main\Lib\Context::getRoute()->getParameterValue('id'); 18 | var_dump(\Bitrix\Main\Lib\Context::getRoute()->getParametersValues()); 19 | })->name('calendar'); 20 | 21 | $routes->get('{id}/news', function () { 22 | echo 'news ' . \Bitrix\Main\Lib\Context::getRoute()->getParameterValue('id'); 23 | var_dump(\Bitrix\Main\Lib\Context::getRoute()->getParametersValues()); 24 | })->name('news'); 25 | }; -------------------------------------------------------------------------------- /bitrix/main/config/test.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'key1' => 'value1', 6 | 'key2' => 'value2', 7 | ], 8 | 'section2' => [ 9 | 'key3' => 'value3', 10 | 'sub1' => [ 11 | 'key4' => 'value4' 12 | ], 13 | 'sub2' => [ 14 | 'key5' => 'value5' 15 | ], 16 | ], 17 | ]; -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/LightV3Layout.php: -------------------------------------------------------------------------------- 1 | [ 13 | 'theme' => 'red', 14 | ] 15 | ]; 16 | } 17 | } -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/footer.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/header.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ slot('pageTitle') }} 5 | 6 | 7 | 8 | 9 | {{ asset('/vendor/twbs/bootstrap/dist/css/bootstrap.css') }} 10 | {{ asset('/vendor/twbs/bootstrap/dist/js/bootstrap.js') }} 11 | {{ asset('resources/template_styles.css') }} 12 | 13 | {{ slot('header') }} 14 | 15 | 16 | {{ extension('Bitrix\\Main\\Components\\JsCore\\Extension') }} 17 | 18 | 19 | 20 |
    {{ showPanel }}
    21 | 22 |
    23 |
    24 |
    25 |
    26 | 27 | {% if not mainPage %} 28 | 31 | {% endif %} 32 | 33 |
    34 | 35 | {{ logoImage}} 36 | 37 | 38 | {% if not mainPage %} 39 |

    {{ pageTitle }}

    40 | {% endif %} 41 | 42 |
    43 |
    44 | 64 |
    65 |
    66 |
    67 |
    68 |
    69 |
    70 |
    71 |

    Интернет-магазин

    72 | Лучшие товары по лучшим ценам! 73 |
    74 |
    75 |
    76 |
    77 |
    78 |
    79 | {% if not orderPage %} 80 |
    81 |
    82 | {{ component(['Bitrix\\Main\\Components\\Menu\\Controller', 'defaultAction']) }} 83 |
    84 |
    85 | 86 |
    87 | {% endif %} -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/layout.twig: -------------------------------------------------------------------------------- 1 | {{ include('header.twig') }} 2 | 3 | {% block workarea %}{% endblock %} 4 | 5 | {{ include('footer.twig') }} -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/bg.jpg -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/delivery.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/editor_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/editor_sprite.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/foooter_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/foooter_bg.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/footer_h4_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/footer_h4_bg.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/header_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/header_1.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/label.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/lightblue_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/lightblue_bg.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/logo.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/logo_mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/logo_mobile.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/logo_mobile_retina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/logo_mobile_retina.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/logo_retina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/logo_retina.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/phone_white.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/ru/preview_horizontal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/ru/preview_horizontal.gif -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/ru/preview_vertical.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/ru/preview_vertical.gif -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/ru/screen_horizontal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/ru/screen_horizontal.gif -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/ru/screen_vertical.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/ru/screen_vertical.gif -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/sale.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/self.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/wt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/wt.png -------------------------------------------------------------------------------- /bitrix/main/layouts/LightV3/resources/images/x2footer_h4_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/layouts/LightV3/resources/images/x2footer_h4_bg.png -------------------------------------------------------------------------------- /bitrix/main/lib/AssetManager.php: -------------------------------------------------------------------------------- 1 | assets[$asset])) 14 | { 15 | $this->assets[$asset] = true; 16 | } 17 | } 18 | 19 | public function render() 20 | { 21 | $env = Response::getTwigEnvironment(); 22 | $stackExt = $env->getExtension(StackExtension::class); 23 | 24 | $output = ''; 25 | 26 | foreach (array_keys($this->assets) as $asset) 27 | { 28 | // identify asset 29 | $path = $this->getResourcePath($asset); 30 | 31 | if (substr($asset, -3) === 'css') 32 | { 33 | $output .= ''."\n"; 34 | } 35 | elseif (substr($asset, -2) === 'js') 36 | { 37 | $output .= ''."\n"; 38 | } 39 | } 40 | 41 | if ($output !== '') 42 | { 43 | //$stackExt->pushStack('header', $output.PHP_EOL); 44 | } 45 | 46 | return $output; 47 | } 48 | 49 | public function getResourcePath($path) 50 | { 51 | $uri = '/resources/'; 52 | 53 | $resourceHash = null; 54 | $resourcePath = $path; 55 | $resourcePathParts = explode('/', ltrim($resourcePath, '/')); 56 | 57 | // guess it is module 58 | // the is no "modules" at the moment 59 | if ($resourcePathParts[1] === 'modules' && !empty($resourcePathParts[2])) 60 | { 61 | $modulePath = $resourcePathParts[0].'/'.$resourcePathParts[1].'/'.$resourcePathParts[2]; 62 | $resourceMetaPath = PROJECT_ROOT.'/'.$modulePath.'/meta/resource.map'; 63 | 64 | if (file_exists($resourceMetaPath)) 65 | { 66 | // it is module with meta, get resource hash 67 | $resourceMeta = new ResourceMeta($resourceMetaPath); 68 | $resourceRelPath = join('/', array_slice($resourcePathParts, 3)); 69 | $resourceHash = $resourceMeta->getHash($resourceRelPath); 70 | 71 | if ($resourceHash !== null) 72 | { 73 | // rewrite filename 74 | $resourceFilename = basename($resourcePath); 75 | 76 | $lastDotPos = strrpos($resourceFilename, '.'); 77 | $resourceName = substr($resourceFilename, 0, $lastDotPos); 78 | $resourceExt = substr($resourceFilename, $lastDotPos + 1); 79 | 80 | $staticResourceFilename = $resourceName.'.'.$resourceHash.'.'.$resourceExt; 81 | $resourcePathParts[count($resourcePathParts)-1] = $staticResourceFilename; 82 | 83 | // return uri with hash 84 | $path = join('/', $resourcePathParts); 85 | } 86 | else 87 | { 88 | // count runtime 89 | // trigger warning 90 | } 91 | } 92 | } 93 | 94 | 95 | if ($resourceHash === null) 96 | { 97 | $resourceAbsPath = PROJECT_ROOT.'/'.ltrim($path, '/'); 98 | 99 | // get resource hash from cache 100 | $resourceHashMap = []; 101 | $cache = Context::getCache(); 102 | $item = $cache->getItem('RESOURCES_HASH_RUNTIME'); 103 | 104 | if ($item->isHit()) 105 | { 106 | $resourceHashMap = $item->get(); 107 | 108 | if (isset($resourceHashMap[$path])) 109 | { 110 | // found resource hash 111 | $resourceHash = $resourceHashMap[$path]; 112 | } 113 | } 114 | 115 | if ($resourceHash === null) 116 | { 117 | // count runtime 118 | $resourceHash = substr(md5_file($resourceAbsPath), 0, 12); 119 | 120 | // put in cache 121 | $resourceHashMap[$path] = $resourceHash; 122 | $item->set($resourceHashMap); 123 | $item->expiresAfter(3600*24*365); 124 | $cache->saveDeferred($item); 125 | } 126 | 127 | // rewrite filename 128 | $resourceFilename = basename($resourcePath); 129 | 130 | $lastDotPos = strrpos($resourceFilename, '.'); 131 | $resourceName = substr($resourceFilename, 0, $lastDotPos); 132 | $resourceExt = substr($resourceFilename, $lastDotPos + 1); 133 | 134 | $staticResourceFilename = $resourceName.'.'.$resourceHash.'.'.$resourceExt; 135 | $resourcePathParts[count($resourcePathParts)-1] = $staticResourceFilename; 136 | 137 | // return uri with hash 138 | $path = join('/', $resourcePathParts); 139 | } 140 | 141 | return $uri.$path; 142 | } 143 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Cache/Adapters/AbstractAdapter.php: -------------------------------------------------------------------------------- 1 | dir = PROJECT_ROOT . '/' . $options['dir']; 33 | $this->lockPolicy = ArrayHelper::get($options, 'lockPolicy', LockPolicy::RETURN_OLD); 34 | } 35 | 36 | public function get($key) 37 | { 38 | $hash = static::getKeyHash($key); 39 | $cacheDir = $this->getSubdirByHash($hash); 40 | $cacheFilePath = "{$cacheDir}/{$hash}"; 41 | 42 | if (!file_exists($cacheFilePath)) 43 | { 44 | return new EmptyResult; 45 | } 46 | 47 | // read data 48 | $cacheContent = file_get_contents($cacheFilePath); 49 | $cacheData = $this->unpackData($cacheContent); 50 | 51 | if (!empty($cacheData) && time() <= $cacheData['exp']) 52 | { 53 | // return valid item 54 | return $cacheData['v']; 55 | } 56 | 57 | // check locking 58 | $cacheFile = fopen($cacheFilePath, 'r'); 59 | 60 | $locked = $this->isFileLocked($cacheFile); 61 | $waitLockRelease = false; 62 | 63 | if (!empty($cacheData) && time() > $cacheData['exp']) 64 | { 65 | // handle expired record 66 | if ($locked) 67 | { 68 | // according to policy 69 | switch ($this->lockPolicy) 70 | { 71 | case LockPolicy::RETURN_EMPTY: 72 | return new EmptyResult; 73 | case LockPolicy::RETURN_OLD: 74 | return $cacheData['v']; 75 | case LockPolicy::WAIT: 76 | // wait 77 | $waitLockRelease = true; 78 | break; 79 | } 80 | } 81 | else 82 | { 83 | // remove expired row 84 | $this->delete($key); 85 | 86 | return new EmptyResult; 87 | } 88 | } 89 | else 90 | { 91 | // handle empty file 92 | if ($locked) 93 | { 94 | // wait for result 95 | $waitLockRelease = true; 96 | } 97 | else 98 | { 99 | // invalid value 100 | $this->delete($key); 101 | 102 | return new EmptyResult; 103 | } 104 | } 105 | 106 | if ($waitLockRelease) 107 | { 108 | // try to read every second 109 | while (true) 110 | { 111 | sleep(1); 112 | 113 | if (!flock($cacheFile, LOCK_EX | LOCK_NB, $wouldBlock)) 114 | { 115 | if ($wouldBlock) 116 | { 117 | // another process holds the lock 118 | continue; 119 | } 120 | } 121 | 122 | // lock obtained. release now 123 | flock($cacheFile, LOCK_UN); 124 | 125 | return $this->get($key); 126 | } 127 | } 128 | } 129 | 130 | public function put($key, $value, $expiresAt) 131 | { 132 | $hash = static::getKeyHash($key); 133 | $cacheDir = $this->getSubdirByHash($hash); 134 | $cacheFilePath = "{$cacheDir}/{$hash}"; 135 | 136 | if (!file_exists($cacheDir)) 137 | { 138 | mkdir($cacheDir, 0777, true); 139 | } 140 | 141 | $cacheData = [ 142 | 'exp' => $expiresAt, 143 | 'v' => $value 144 | ]; 145 | 146 | $cacheContent = $this->packData($cacheData); 147 | 148 | return (bool) file_put_contents($cacheFilePath, $cacheContent); 149 | } 150 | 151 | public function delete($key) 152 | { 153 | $hash = static::getKeyHash($key); 154 | $cacheDir = $this->getSubdirByHash($hash); 155 | $cacheFilePath = "{$cacheDir}/{$hash}"; 156 | 157 | unlink($cacheFilePath); 158 | } 159 | 160 | public function deleteAll() 161 | { 162 | $files = new RecursiveIteratorIterator( 163 | new RecursiveDirectoryIterator($this->dir, RecursiveDirectoryIterator::SKIP_DOTS), 164 | RecursiveIteratorIterator::CHILD_FIRST 165 | ); 166 | 167 | foreach ($files as $fileInfo) 168 | { 169 | $todo = ($fileInfo->isDir() ? 'rmdir' : 'unlink'); 170 | $todo($fileInfo->getRealPath()); 171 | } 172 | } 173 | 174 | public function setLock($key) 175 | { 176 | $hash = static::getKeyHash($key); 177 | $cacheDir = $this->getSubdirByHash($hash); 178 | $cacheFilePath = "{$cacheDir}/{$hash}"; 179 | 180 | // create empty file 181 | file_put_contents($cacheFilePath, ''); 182 | 183 | // lock it 184 | $cacheFile = fopen($cacheFilePath, 'r'); 185 | flock($cacheFile, LOCK_EX | LOCK_NB, $wouldBlock); 186 | 187 | // save resource for unlock 188 | $this->currentlyLocked[$key] = $cacheFile; 189 | } 190 | 191 | public function releaseLock($key) 192 | { 193 | if (empty($this->currentlyLocked[$key])) 194 | { 195 | return; 196 | } 197 | 198 | $cacheFile = $this->currentlyLocked[$key]; 199 | flock($cacheFile, LOCK_UN); 200 | fclose($cacheFile); 201 | } 202 | 203 | protected function isFileLocked($cacheFile) 204 | { 205 | $locked = false; 206 | 207 | if (!flock($cacheFile, LOCK_EX | LOCK_NB, $wouldBlock)) 208 | { 209 | if ($wouldBlock) 210 | { 211 | // another process holds the lock 212 | $locked = true; 213 | } 214 | } 215 | else 216 | { 217 | // lock obtained. release now 218 | flock($cacheFile, LOCK_UN); 219 | } 220 | 221 | return $locked; 222 | } 223 | 224 | public function getSubdirByHash($hash) 225 | { 226 | $subdir = substr($hash, -3); 227 | 228 | return "{$this->dir}/{$subdir}"; 229 | } 230 | 231 | public function packData($data) 232 | { 233 | return serialize($data); 234 | } 235 | 236 | public function unpackData($content) 237 | { 238 | return unserialize($content); 239 | } 240 | 241 | public static function getKeyHash($key) 242 | { 243 | return md5($key); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /bitrix/main/lib/Cache/Adapters/LockableAdapterInterface.php: -------------------------------------------------------------------------------- 1 | resource = new \Memcache; 29 | 30 | $host = ArrayHelper::get($options, 'host', 'localhost'); 31 | $port = ArrayHelper::get($options, 'port', '11211'); 32 | $this->resource->addServer($host, $port); 33 | 34 | $this->lockPolicy = ArrayHelper::get($options, 'lockPolicy', LockPolicy::RETURN_OLD); 35 | } 36 | 37 | public function get($key) 38 | { 39 | $result = $this->resource->get($key); 40 | 41 | if ($result === false) 42 | { 43 | $locked = (bool) $this->resource->get('lock_' . $key); 44 | 45 | if ($locked) 46 | { 47 | // according to policy 48 | switch ($this->lockPolicy) 49 | { 50 | case LockPolicy::RETURN_EMPTY: 51 | return new EmptyResult; 52 | case LockPolicy::RETURN_OLD: 53 | case LockPolicy::WAIT: 54 | // try to read every second 55 | sleep(1); 56 | return $this->get($key); 57 | } 58 | } 59 | else 60 | { 61 | return new EmptyResult; 62 | } 63 | } 64 | 65 | return $result; 66 | } 67 | 68 | public function put($key, $value, $expiresAt) 69 | { 70 | $this->resource->set($key, $value, 0, $expiresAt - time()); 71 | } 72 | 73 | public function delete($key) 74 | { 75 | $this->resource->delete($key); 76 | } 77 | 78 | public function deleteAll() 79 | { 80 | $this->resource->flush(); 81 | } 82 | 83 | public function setLock($key) 84 | { 85 | $this->put('lock_' . $key, '1', time() + 3600); 86 | } 87 | 88 | public function releaseLock($key) 89 | { 90 | $this->resource->delete('lock_' . $key); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /bitrix/main/lib/Cache/Cache.php: -------------------------------------------------------------------------------- 1 | getItem($key); 24 | 25 | // return if got cached value 26 | if ($item->isHit()) 27 | { 28 | return $item->get(); 29 | } 30 | 31 | // set global lock 32 | if ($cache->getAdapter() instanceof LockableAdapterInterface) 33 | { 34 | $cache->setLock($key); 35 | } 36 | 37 | // get value and cache it 38 | $value = call_user_func($valueCallback, $item); 39 | 40 | if ($value instanceof EmptyResult) 41 | { 42 | return null; 43 | } 44 | 45 | $item->set($value); 46 | $cache->save($item); 47 | 48 | return $value; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /bitrix/main/lib/Cache/EmptyResult.php: -------------------------------------------------------------------------------- 1 | key = $key; 35 | $this->value = $value; 36 | $this->isHit = (bool) $isHit; 37 | } 38 | 39 | public function getKey() 40 | { 41 | return $this->key; 42 | } 43 | 44 | public function get() 45 | { 46 | return $this->value; 47 | } 48 | 49 | public function isHit() 50 | { 51 | return $this->isHit; 52 | } 53 | 54 | public function set($value) 55 | { 56 | $this->value = $value; 57 | } 58 | 59 | public function expiresAt($expiration) 60 | { 61 | $this->expire = $expiration; 62 | } 63 | 64 | public function expiresAfter($time) 65 | { 66 | $this->expire = time() + $time; 67 | } 68 | 69 | public function getExpiresAt() 70 | { 71 | return $this->expire; 72 | } 73 | 74 | public function setIsHit($isHit = true) 75 | { 76 | $this->isHit = (bool) $isHit; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /bitrix/main/lib/Cache/LockPolicy.php: -------------------------------------------------------------------------------- 1 | adapter = $adapter; 32 | } 33 | 34 | public function getItem($key) 35 | { 36 | // get from internal pool 37 | if (isset($this->items[$key])) 38 | { 39 | return $this->items[$key]; 40 | } 41 | 42 | // get from adapter 43 | $isHit = true; 44 | $value = $this->adapter->get($key); 45 | 46 | if ($value instanceof EmptyResult) 47 | { 48 | $isHit = false; 49 | $value = null; 50 | } 51 | 52 | $item = new Item($key, $value, $isHit); 53 | $this->items[$key] = $item; 54 | 55 | return $item; 56 | } 57 | 58 | public function getItems(array $keys = array()) 59 | { 60 | $items = []; 61 | 62 | foreach ($keys as $key) 63 | { 64 | $items[] = $this->getItem($key); 65 | } 66 | 67 | return $items; 68 | 69 | // TODO: Implement MultiGetAdapterInterface 70 | } 71 | 72 | public function hasItem($key) 73 | { 74 | $item = $this->getItem($key); 75 | 76 | return $item->isHit(); 77 | 78 | // TODO: Implement HasItemAdapterInterface 79 | } 80 | 81 | public function clear() 82 | { 83 | $this->adapter->deleteAll(); 84 | } 85 | 86 | public function deleteItem($key) 87 | { 88 | $this->adapter->delete($key); 89 | unset($this->items[$key]); 90 | } 91 | 92 | public function deleteItems(array $keys) 93 | { 94 | foreach ($keys as $key) 95 | { 96 | $this->deleteItem($key); 97 | } 98 | 99 | // TODO: Implement MultiDeleteAdapterInterface 100 | } 101 | 102 | public function setLock($key) 103 | { 104 | $this->adapter->setLock($key); 105 | } 106 | 107 | public function save(\Psr\Cache\CacheItemInterface $item) 108 | { 109 | /** @var $item Item */ 110 | $result = $this->adapter->put( 111 | $item->getKey(), 112 | $item->get(), 113 | $item->getExpiresAt() 114 | ); 115 | 116 | // unlock 117 | if ($this->adapter instanceof LockableAdapterInterface) 118 | { 119 | $this->adapter->releaseLock($item->getKey()); 120 | } 121 | 122 | // actualize pool 123 | $item->setIsHit(true); 124 | $this->items[$item->getKey()] = $item; 125 | } 126 | 127 | public function saveDeferred(\Psr\Cache\CacheItemInterface $item) 128 | { 129 | /** @var $item Item */ 130 | $this->itemsToSave[$item->getKey()] = $item; 131 | 132 | // actualize pool 133 | $item->setIsHit(true); 134 | $this->items[$item->getKey()] = $item; 135 | } 136 | 137 | public function commit() 138 | { 139 | foreach ($this->itemsToSave as $item) 140 | { 141 | $this->save($item); 142 | } 143 | 144 | $this->itemsToSave = []; 145 | } 146 | 147 | /** 148 | * @return AbstractAdapter 149 | */ 150 | public function getAdapter(): AbstractAdapter 151 | { 152 | return $this->adapter; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /bitrix/main/lib/Configuration/Configuration.php: -------------------------------------------------------------------------------- 1 | configDir = PROJECT_ROOT.'/config'; 27 | } 28 | 29 | /** 30 | * Returns configuration value by key "config.key1.key2" or "module:config.key1.key2" 31 | * 32 | * @param $key 33 | * 34 | * @return mixed 35 | */ 36 | public function get($key) 37 | { 38 | [$module, $configName, $parts] = static::parseKey($key); 39 | $configuration = $this->includeConfig($configName, $module); 40 | 41 | foreach ($parts as $part) 42 | { 43 | if (array_key_exists($part, $configuration)) 44 | { 45 | $configuration = $configuration[$part]; 46 | } 47 | else 48 | { 49 | return null; 50 | } 51 | } 52 | 53 | return $configuration; 54 | } 55 | 56 | /** 57 | * Returns merged configuration from site, site parent, project, module. 58 | * 59 | * @param ?Module $module 60 | * @param string $configName 61 | * 62 | * @return array|RewriteValue|mixed 63 | */ 64 | protected function includeConfig(string $configName, ?Module $module = null) 65 | { 66 | $configurations = []; 67 | 68 | // module 69 | if (!empty($module)) 70 | { 71 | $configPath = $module->getConfigPath().'/'.$configName.'.php'; 72 | if (file_exists($configPath)) 73 | { 74 | $configurations[] = include $configPath; 75 | } 76 | } 77 | 78 | // project 79 | $configPath = $this->configDir.'/'; 80 | 81 | if (!empty($module)) 82 | { 83 | $configPath .= $module->getName().'/'; 84 | } 85 | 86 | $configPath .= $configName.'.php'; 87 | 88 | if (file_exists($configPath)) 89 | { 90 | $configurations[] = include $configPath; 91 | } 92 | 93 | $site = Context::getSite(); 94 | 95 | // site and its parent 96 | if ($site) 97 | { 98 | $siteParent = $site->getParent(); 99 | 100 | if ($siteParent) 101 | { 102 | $configPath = $siteParent->getConfigPath().'/'.$configName.'.php'; 103 | if (file_exists($configPath)) 104 | { 105 | $configurations[] = include $configPath; 106 | } 107 | } 108 | 109 | $configPath = $site->getConfigPath().'/'.$configName.'.php'; 110 | if (file_exists($configPath)) 111 | { 112 | $configurations[] = include $configPath; 113 | } 114 | } 115 | 116 | // merge 117 | return $this->merge($configurations); 118 | } 119 | 120 | /** 121 | * Merges few configurations into one 122 | * 123 | * @param array $configurations 124 | * 125 | * @return array 126 | */ 127 | protected function merge(array $configurations) 128 | { 129 | $baseConfiguration = array_shift($configurations); 130 | 131 | if (!empty($configurations)) 132 | { 133 | // merge each with base 134 | foreach ($configurations as $configuration) 135 | { 136 | $baseConfiguration = $this->mergeWithChild($baseConfiguration, $configuration); 137 | } 138 | } 139 | else 140 | { 141 | $baseConfiguration = $baseConfiguration ?? []; 142 | } 143 | 144 | return $baseConfiguration; 145 | } 146 | 147 | /** 148 | * Merges two configurations. 149 | * 150 | * @param array|RewriteValue $baseConfiguration 151 | * @param array|RewriteValue $childConfiguration 152 | * 153 | * @return array 154 | */ 155 | protected function mergeWithChild($baseConfiguration, $childConfiguration) 156 | { 157 | if ($childConfiguration instanceof RewriteValue) 158 | { 159 | return static::unpackRewriteValues($childConfiguration); 160 | } 161 | 162 | foreach ($childConfiguration as $key => $value) 163 | { 164 | if ( (is_array($value) && !ArrayHelper::hasIntKeys($value)) 165 | || $value instanceof RewriteValue 166 | ) 167 | { 168 | // sub configuration 169 | if (isset($baseConfiguration[$key])) 170 | { 171 | $baseConfiguration[$key] = $this->mergeWithChild($baseConfiguration[$key], $value); 172 | } 173 | else 174 | { 175 | $baseConfiguration[$key] = $value instanceof RewriteValue 176 | ? $value->getValues() 177 | : $value; 178 | } 179 | 180 | } 181 | else 182 | { 183 | // just a value 184 | $baseConfiguration[$key] = $value; 185 | } 186 | } 187 | 188 | return $baseConfiguration; 189 | } 190 | 191 | /** 192 | * Recursively converts RewriteValue objects into arrays 193 | * 194 | * @param array|RewriteValue $value 195 | * 196 | * @return array 197 | */ 198 | public static function unpackRewriteValues($value) 199 | { 200 | $value = $value instanceof RewriteValue 201 | ? $value->getValues() 202 | : $value; 203 | 204 | foreach ($value as $k => $v) 205 | { 206 | if (is_array($v) || $v instanceof RewriteValue) 207 | { 208 | $value[$k] = static::unpackRewriteValues($v); 209 | } 210 | } 211 | 212 | return $value; 213 | } 214 | 215 | /** 216 | * Receives key "config.key1.key2" or "module:config.key1.key2" 217 | * and returns [Module $module, string $configName, array $parts] 218 | * where $parts would be ["key1", "key2"] 219 | * 220 | * @param $key 221 | * 222 | * @return array|Module[] 223 | */ 224 | public static function parseKey($key) 225 | { 226 | // parse module 227 | $colonPos = strpos($key, ':'); 228 | 229 | if ($colonPos) 230 | { 231 | $moduleFqn = substr($key, 0, $colonPos); 232 | $configKey = substr($key, $colonPos + 1); 233 | 234 | $module = Module::createFromString($moduleFqn); 235 | } 236 | else 237 | { 238 | $module = null; 239 | $configKey = $key; 240 | } 241 | 242 | // parse config key 243 | $parts = explode('.', $configKey); 244 | $configName = array_shift($parts); 245 | 246 | return [$module, $configName, $parts]; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /bitrix/main/lib/Configuration/Controller/Attributes/ComponentAlias.php: -------------------------------------------------------------------------------- 1 | routeNames; 19 | } 20 | 21 | /** 22 | * @param array $routeNames 23 | */ 24 | public function setRouteNames(array $routeNames): void 25 | { 26 | $this->routeNames = $routeNames; 27 | } 28 | 29 | /** 30 | * @return array 31 | */ 32 | public function getComponentAliases(): array 33 | { 34 | return $this->componentAliases; 35 | } 36 | 37 | /** 38 | * @param array $componentAliases 39 | */ 40 | public function setComponentAliases(array $componentAliases): void 41 | { 42 | $this->componentAliases = $componentAliases; 43 | } 44 | 45 | /** 46 | * @return \Closure 47 | */ 48 | public function getCallback(): \Closure 49 | { 50 | return $this->callback; 51 | } 52 | 53 | /** 54 | * @param \Closure $callback 55 | */ 56 | public function setCallback(\Closure $callback): void 57 | { 58 | $this->callback = $callback; 59 | } 60 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Configuration/Controller/ControllerConfiguration.php: -------------------------------------------------------------------------------- 1 | loadConfiguration(); 25 | 26 | // search by index for current class with current route scope 27 | $controllerClass = get_class($controller); 28 | $routeName = Context::getRoute()?->getOptions()->getFullName(); 29 | 30 | /** @var ConfigurationItem[] $configItems */ 31 | $configItems = []; 32 | 33 | // 1st level filter 34 | if ($routeName > '') 35 | { 36 | if (!empty($this->routeNameScope[$controllerClass][$routeName])) 37 | { 38 | $configItems = array_merge($configItems, $this->routeNameScope[$controllerClass][$routeName]); 39 | } 40 | } 41 | 42 | if ($componentAlias > '') 43 | { 44 | if (!empty($this->componentAliasScope[$controllerClass][$componentAlias])) 45 | { 46 | $configItems = array_merge($configItems, $this->componentAliasScope[$controllerClass][$componentAlias]); 47 | } 48 | } 49 | 50 | // 2nd level filter 51 | foreach ($configItems as $k => $configItem) 52 | { 53 | if ($routeName > '' || !empty($configItem->getRouteNames())) 54 | { 55 | if (!in_array($routeName, $configItem->getRouteNames())) 56 | { 57 | unset($configItems[$k]); 58 | } 59 | } 60 | 61 | if ($componentAlias > '' || !empty($configItem->getComponentAliases())) 62 | { 63 | if (!in_array($componentAlias, $configItem->getComponentAliases())) 64 | { 65 | unset($configItems[$k]); 66 | } 67 | } 68 | } 69 | 70 | // global 71 | if (!empty($this->globalScope[$controllerClass])) 72 | { 73 | $configItems = array_merge($this->globalScope[$controllerClass], $configItems); 74 | } 75 | 76 | // configure controller 77 | foreach ($configItems as $configItem) 78 | { 79 | $configItem->getCallback()($controller); 80 | } 81 | } 82 | 83 | public function loadConfiguration() 84 | { 85 | if ($this->loaded) 86 | { 87 | return; 88 | } 89 | 90 | // get callbacks from files 91 | $configuration = Context::getContainer()->get(Configuration::class); 92 | $configFiles = [PROJECT_ROOT.'/config/controllers/forum.php']; // TODO all files from dir 93 | 94 | foreach ($configFiles as $configFile) 95 | { 96 | $callbacks = include $configFile; 97 | 98 | foreach ($callbacks as $callback) 99 | { 100 | $reflection = new ReflectionFunction($callback); 101 | $attributes = $reflection->getAttributes(); 102 | 103 | $configurationItem = new ConfigurationItem(); 104 | $configurationItem->setCallback($callback); 105 | 106 | // controller class 107 | $parameter = current($reflection->getParameters()); 108 | $controllerClass = $parameter->getType()->getName(); 109 | 110 | // global scope 111 | if (empty($attributes)) 112 | { 113 | $this->globalScope[$controllerClass][] = $configurationItem; 114 | } 115 | 116 | // attributes 117 | foreach ($attributes as $attribute) 118 | { 119 | switch ($attribute->getName()) 120 | { 121 | case RouteName::class: 122 | $routeNames = $attribute->getArguments(); 123 | $configurationItem->setRouteNames($routeNames); 124 | 125 | foreach ($routeNames as $routeName) 126 | { 127 | $this->routeNameScope[$controllerClass][$routeName][] = $configurationItem; 128 | } 129 | 130 | break; 131 | 132 | case ComponentAlias::class: 133 | $componentAliases = $attribute->getArguments(); 134 | $configurationItem->setComponentAliases($componentAliases); 135 | 136 | foreach ($componentAliases as $componentAlias) 137 | { 138 | $this->componentAliasScope[$controllerClass][$componentAlias][] = $configurationItem; 139 | } 140 | 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | 147 | $this->loaded = true; 148 | 149 | // TODO put in cache 150 | } 151 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Configuration/RewriteValue.php: -------------------------------------------------------------------------------- 1 | values = $values; 25 | } 26 | 27 | /** 28 | * @return array 29 | */ 30 | public function getValues(): array 31 | { 32 | return $this->values; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bitrix/main/lib/Context.php: -------------------------------------------------------------------------------- 1 | addDefinitions($definition); 72 | } 73 | 74 | static::$container = $builder->build(); 75 | } 76 | 77 | return static::$container; 78 | } 79 | 80 | public static function getConfiguration() 81 | { 82 | if (static::$configuration === null) 83 | { 84 | static::$configuration = new Configuration; 85 | } 86 | 87 | return static::$configuration; 88 | } 89 | 90 | public static function setSite(Site $site) 91 | { 92 | static::$site = $site; 93 | } 94 | 95 | /** 96 | * @return Site 97 | */ 98 | public static function getSite() 99 | { 100 | return static::$site; 101 | } 102 | 103 | public static function unsetSite() 104 | { 105 | static::$site = null; 106 | } 107 | 108 | /** 109 | * @return AssetManager 110 | */ 111 | public static function getAssetManager() 112 | { 113 | if (static::$assetManager === null) 114 | { 115 | $assetManager = new AssetManager; 116 | 117 | static::getEventManager()->addEventHandler( 118 | 'main', 119 | 'onPageRender', 120 | function (Event $event) use ($assetManager) { 121 | /** @var StackExtension $ext */ 122 | $ext = $event->getParameter(StackExtension::class); 123 | $ext->pushStack('header', $assetManager->render(), true); 124 | } 125 | ); 126 | 127 | static::$assetManager = $assetManager; 128 | } 129 | 130 | return static::$assetManager; 131 | } 132 | 133 | /** 134 | * @param AssetManager $assetManager 135 | */ 136 | public static function setAssetManager($assetManager) 137 | { 138 | static::$assetManager = $assetManager; 139 | } 140 | 141 | /** 142 | * @return EventManager 143 | */ 144 | public static function getEventManager() 145 | { 146 | if (static::$eventManager === null) 147 | { 148 | static::$eventManager = EventManager::getInstance(); 149 | } 150 | 151 | return self::$eventManager; 152 | } 153 | 154 | /** 155 | * @param EventManager $eventManager 156 | */ 157 | public static function setEventManager($eventManager) 158 | { 159 | self::$eventManager = $eventManager; 160 | } 161 | 162 | /** 163 | * @return ?Route 164 | */ 165 | public static function getRoute(): ?Route 166 | { 167 | return self::$route; 168 | } 169 | 170 | /** 171 | * @param Route $route 172 | */ 173 | public static function setRoute(Route $route): void 174 | { 175 | self::$route = $route; 176 | } 177 | 178 | /** 179 | * @return Router 180 | */ 181 | public static function getRouter(): Router 182 | { 183 | return self::$router; 184 | } 185 | 186 | /** 187 | * @param Router $router 188 | */ 189 | public static function setRouter(Router $router): void 190 | { 191 | self::$router = $router; 192 | } 193 | 194 | public static function getCache($poolName = 'default'): Pool 195 | { 196 | if (!isset(static::$cachePools[$poolName])) 197 | { 198 | //$config = static::$configuration->get('cache.'.$poolName); 199 | $config = static::getConfiguration()->get('cache.'.$poolName); 200 | 201 | if (empty($config)) 202 | { 203 | throw new \Exception; 204 | } 205 | 206 | $adapterClass = $config['adapter']; 207 | //$pool = new Pool(new $adapterClass(PROJECT_ROOT.'/cache')); 208 | $pool = new Pool(new $adapterClass($config['options'])); 209 | 210 | static::$cachePools[$poolName] = $pool; 211 | static::$kernel->registerTerminateHandler(function () use ($pool) { 212 | $pool->commit(); 213 | }); 214 | } 215 | 216 | return static::$cachePools[$poolName]; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /bitrix/main/lib/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | templateRouterContext; 24 | } 25 | 26 | /** 27 | * @param mixed $templateRouterContext 28 | */ 29 | public function setTemplateRouterContext(array $templateRouterContext): void 30 | { 31 | $this->templateRouterContext = $templateRouterContext; 32 | } 33 | 34 | public function render($viewName, $context = []): string 35 | { 36 | $template = $this->getPath() . '/templates/'.$viewName; 37 | $templateDir = dirname($template); 38 | 39 | // include default assets 40 | $templateStyle = $templateDir.'/resources/css/style.css'; 41 | if (file_exists($templateStyle)) 42 | { 43 | $styleInProject = str_replace(PROJECT_ROOT, '', $templateStyle); 44 | 45 | $env = Response::getTwigEnvironment(); 46 | /** @var BitrixExtension $bitrixExt */ 47 | $bitrixExt = $env->getExtension(BitrixExtension::class); 48 | $bitrixExt->includeAsset($styleInProject); 49 | } 50 | 51 | // add template in context 52 | $tpl = new Template($this->templateRouterContext); 53 | $context['this'] = $tpl; 54 | 55 | return (new Response($template, $context))->render(); 56 | } 57 | 58 | protected function forwardNotFound($customMessageId = null) 59 | { 60 | $router = Context::getRouter(); 61 | $fallback = $router->getFallbackNotFound(); 62 | 63 | // TODO use $fallback from router? 64 | throw new ErrorResponseException($customMessageId, 404/*, '404.twig'*/); 65 | } 66 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Controllers/ControllerFactory.php: -------------------------------------------------------------------------------- 1 | container = $container; 19 | } 20 | 21 | /** 22 | * @template T 23 | * @param string|class-string $controllerClass 24 | * @param ?string $configurationAlias 25 | * @return mixed|T 26 | * 27 | * @throws DependencyException 28 | * @throws NotFoundException 29 | */ 30 | public function create(string $controllerClass, string $configurationAlias = null, Route $route = null): Controller 31 | { 32 | /** @var Controller $controller */ 33 | $controller = $this->container->make($controllerClass); 34 | 35 | // 1. configuration alias 36 | if (!empty($configurationAlias)) 37 | { 38 | $controller->setTemplateRouterContext(['configuration' => $configurationAlias]); 39 | } 40 | 41 | // 2. get solution from current route, get mapped parameters for this controller 42 | $route = $route ?: Context::getRoute(); 43 | 44 | if ($route?->getOptions()->hasSolution()) 45 | { 46 | $route->getOptions()->getSolution()->setParametersToController($controller); 47 | } 48 | 49 | // 3. custom configuration for controller 50 | $controllerConfiguration = $this->container->get(ControllerConfiguration::class); 51 | $controllerConfiguration->setConfiguration($controller, $configurationAlias); 52 | 53 | return $controller; 54 | } 55 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Controllers/Extension.php: -------------------------------------------------------------------------------- 1 | getFileName()); 23 | $dirInProject = str_replace(PROJECT_ROOT, '', $dirInProject); 24 | 25 | // get also dependencies 26 | 27 | foreach ($this->getJs() as $jsAsset) 28 | { 29 | $assets[] = $dirInProject.'/'.$jsAsset; 30 | } 31 | 32 | foreach ($this->getCss() as $cssAsset) 33 | { 34 | $assets[] = $dirInProject.'/'.$cssAsset; 35 | } 36 | 37 | return $assets; 38 | } 39 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Event.php: -------------------------------------------------------------------------------- 1 | moduleId = $moduleId; 32 | $this->type = $type; 33 | $this->setParameters($parameters); 34 | $this->setFilter($filter); 35 | 36 | $this->debugMode = false; 37 | } 38 | 39 | public function getModuleId() 40 | { 41 | return $this->moduleId; 42 | } 43 | 44 | public function getEventType() 45 | { 46 | return $this->type; 47 | } 48 | 49 | public function setParameters($parameters) 50 | { 51 | if (is_array($parameters)) 52 | { 53 | $this->parameters = $parameters; 54 | } 55 | elseif ($parameters instanceof \Closure) 56 | { 57 | $this->parameters = null; 58 | $this->parametersLoader = $parameters; 59 | } 60 | else 61 | { 62 | throw new ArgumentTypeException("parameter", "array or closure, which returns array"); 63 | } 64 | } 65 | 66 | public function getParameters() 67 | { 68 | $this->loadParameters(); 69 | 70 | return $this->parameters; 71 | } 72 | 73 | public function setParameter($key, $value) 74 | { 75 | $this->loadParameters(); 76 | 77 | $this->parameters[$key] = $value; 78 | } 79 | 80 | public function getParameter($key) 81 | { 82 | $this->loadParameters(); 83 | 84 | if (isset($this->parameters[$key])) 85 | return $this->parameters[$key]; 86 | 87 | return null; 88 | } 89 | 90 | protected function loadParameters() 91 | { 92 | if (!$this->parametersLoader) 93 | { 94 | return false; 95 | } 96 | 97 | $this->setParameters(call_user_func_array($this->parametersLoader, array())); 98 | $this->parametersLoader = null; 99 | 100 | return true; 101 | } 102 | 103 | public function setFilter($filter) 104 | { 105 | if (!is_array($filter)) 106 | { 107 | if (empty($filter)) 108 | $filter = null; 109 | else 110 | $filter = array($filter); 111 | } 112 | 113 | $this->filter = $filter; 114 | } 115 | 116 | public function getFilter() 117 | { 118 | return $this->filter; 119 | } 120 | 121 | /** 122 | * @return EventResult[] 123 | */ 124 | public function getResults() 125 | { 126 | return $this->results; 127 | } 128 | 129 | public function addResult(EventResult $result) 130 | { 131 | $this->results[] = $result; 132 | } 133 | 134 | public function getSender() 135 | { 136 | return $this->sender; 137 | } 138 | 139 | public function send($sender = null) 140 | { 141 | $this->sender = $sender; 142 | EventManager::getInstance()->send($this); 143 | } 144 | 145 | public function addException(\Exception $exception) 146 | { 147 | $this->exceptions[] = $exception; 148 | } 149 | 150 | public function getExceptions() 151 | { 152 | return $this->exceptions; 153 | } 154 | 155 | public function turnDebugOn() 156 | { 157 | $this->debugMode = true; 158 | } 159 | 160 | public function isDebugOn() 161 | { 162 | return $this->debugMode; 163 | } 164 | 165 | public function addDebugInfo($ar) 166 | { 167 | if (!$this->debugMode) 168 | return; 169 | 170 | $this->debugInfo[] = $ar; 171 | } 172 | 173 | public function getDebugInfo() 174 | { 175 | return $this->debugInfo; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /bitrix/main/lib/EventResult.php: -------------------------------------------------------------------------------- 1 | type = $type; 18 | $this->moduleId = $moduleId; 19 | $this->handler = $handler; 20 | $this->parameters = $parameters; 21 | } 22 | 23 | /** @deprecated Use getType() */ 24 | public function getResultType() 25 | { 26 | return $this->getType(); 27 | } 28 | 29 | public function getType() 30 | { 31 | return $this->type; 32 | } 33 | 34 | public function getModuleId() 35 | { 36 | return $this->moduleId; 37 | } 38 | 39 | public function getHandler() 40 | { 41 | return $this->handler; 42 | } 43 | 44 | public function getParameters() 45 | { 46 | return $this->parameters; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bitrix/main/lib/Exceptions/ArgumentException.php: -------------------------------------------------------------------------------- 1 | parameter = $parameter; 22 | } 23 | 24 | public function getParameter() 25 | { 26 | return $this->parameter; 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /bitrix/main/lib/Exceptions/ErrorResponseException.php: -------------------------------------------------------------------------------- 1 | controller = Context::getContainer()->get(RouteControllerFactory::class)->create($controller); 19 | } 20 | else 21 | { 22 | // default value according to code 23 | if (!in_array($code, ErrorPagesController::$codes)) 24 | { 25 | $code = 503; 26 | } 27 | 28 | $actionName = "show{$code}Error"; 29 | $this->controller = Context::getContainer()->get(RouteControllerFactory::class)->create([ErrorPagesController::class, $actionName]); 30 | 31 | // reset site context 32 | Context::unsetSite(); 33 | } 34 | 35 | parent::__construct($message, $code, $previous); 36 | } 37 | 38 | /** 39 | * @return mixed 40 | */ 41 | public function getController(): ?Controller 42 | { 43 | if (!isset($this->controller)) 44 | { 45 | $code = $this->code; 46 | 47 | // default value according to code 48 | if (!in_array($code, ErrorPagesController::$codes)) 49 | { 50 | $code = 503; 51 | } 52 | 53 | $actionName = "show{$code}Error"; 54 | $this->controller = Context::getContainer()->get(RouteControllerFactory::class)->create([ErrorPagesController::class, $actionName]); 55 | 56 | // reset site context 57 | Context::unsetSite(); 58 | } 59 | 60 | return $this->controller; 61 | } 62 | 63 | /** 64 | * @param mixed $controller 65 | */ 66 | public function setController(Controller $controller): void 67 | { 68 | $this->controller = $controller; 69 | } 70 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Exceptions/SystemException.php: -------------------------------------------------------------------------------- 1 | file = $file; 25 | $this->line = $line; 26 | } 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /bitrix/main/lib/ExtensionLoader.php: -------------------------------------------------------------------------------- 1 | container = Context::getContainer(); 38 | } 39 | 40 | public function handle(ServerRequestInterface $request): ResponseInterface 41 | { 42 | // router init 43 | $routes = $this->container->make(RoutingConfigurator::class); 44 | $router = $this->container->make(Router::class); 45 | $routes->setRouter($router); 46 | 47 | Context::setRouter($router); 48 | 49 | // router configuration 50 | $files[] = PROJECT_ROOT.'/config/routes/web.php'; // TODO all files from dir 51 | foreach ($files as $file) 52 | { 53 | $callback = include $file; 54 | $callback($routes); 55 | } 56 | 57 | // router compile 58 | $router->releaseRoutes(); 59 | 60 | // cache for route compiled data 61 | //CompileCache::handle($files, $router); 62 | 63 | $requestHandler = new QueueRequestHandler; 64 | 65 | // add default middleware from config 66 | $middlewares = $this->container->get(Configuration::class)->get('middleware'); 67 | 68 | if (!empty($middlewares) && is_array($middlewares)) 69 | { 70 | foreach ($middlewares as $middleware) 71 | { 72 | $middleware = is_object($middleware) 73 | ? $middleware 74 | : $this->container->make($middleware); 75 | 76 | $requestHandler->addMiddleware($middleware); 77 | } 78 | } 79 | 80 | // add routing middleware - matching, specific by route, calling controller 81 | $routeMatchMiddleware = new RouteMatchMiddleware($router); 82 | $requestHandler->addMiddleware($routeMatchMiddleware); 83 | 84 | // execute middleware and route matching 85 | try 86 | { 87 | $response = $requestHandler->handle($request); 88 | } 89 | catch (\Exception $e) 90 | { 91 | return $this->handleThrowable($e); 92 | } 93 | 94 | return $response; 95 | } 96 | 97 | protected function handleThrowable(\Throwable $e): ResponseInterface 98 | { 99 | if ($e instanceof RuntimeError && $e->getPrevious() instanceof ErrorResponseException) 100 | { 101 | return $this->handleThrowable($e->getPrevious()); 102 | } 103 | 104 | if ($e instanceof ErrorResponseException) 105 | { 106 | // reset output buffer 107 | $env = Response::getTwigEnvironment(); 108 | 109 | /** @var StackExtension $stackExt */ 110 | $stackExt = $env->getExtension(StackExtension::class); 111 | $stackExt->reset(); 112 | 113 | try 114 | { 115 | // generate response, get custom controller from exception 116 | $content = $e->getController()->execute(); 117 | 118 | if ($content instanceof \GuzzleHttp\Psr7\Response) 119 | { 120 | return $content; 121 | } 122 | 123 | return new \GuzzleHttp\Psr7\Response(status: $e->getCode(), body: $content); 124 | } 125 | catch (\Exception $e) 126 | { 127 | return new \GuzzleHttp\Psr7\Response(status: 503, body: 'logged error'); 128 | } 129 | } 130 | 131 | return $this->renderThrowable($e); 132 | } 133 | 134 | public function renderThrowable(\Throwable $e): ResponseInterface 135 | { 136 | // default: show trace for dev, log and show 503 default for prod 137 | $env = $this->container->get(Configuration::class)->get('app.environment'); 138 | 139 | if ($env === 'dev') 140 | { 141 | if ($this->container->has(Run::class)) 142 | { 143 | $whoops = $this->container->get(Run::class); 144 | $html = $whoops->handleException($e); 145 | 146 | return new \GuzzleHttp\Psr7\Response(status: 503, body: $html); 147 | } 148 | else 149 | { 150 | // php output 151 | throw $e; 152 | } 153 | } 154 | else 155 | { 156 | $logger = $this->container->get(LoggerInterface::class); 157 | $logger->error($e); 158 | 159 | // show 503 error 160 | return $this->handleThrowable(new ErrorResponseException($e->getMessage(), 503)); 161 | } 162 | } 163 | 164 | public function registerTerminateHandler($callable) 165 | { 166 | $this->terminateHandlers[] = $callable; 167 | } 168 | 169 | public function terminate() 170 | { 171 | foreach ($this->terminateHandlers as $handler) 172 | { 173 | call_user_func($handler); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /bitrix/main/lib/Layout.php: -------------------------------------------------------------------------------- 1 | getProjectPath() . '/' . $this->getFileName(); 17 | } 18 | 19 | public function getData() 20 | { 21 | return []; 22 | } 23 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Middleware/Middleware.php: -------------------------------------------------------------------------------- 1 | id === (int) $id) 12 | { 13 | return $article; 14 | } 15 | } 16 | 17 | return null; 18 | } 19 | 20 | public static function getLast($limit = 5) 21 | { 22 | $news = static::getList(); 23 | uasort($news, function ($a, $b) { 24 | return $a->id > $b->id ? -1: 1; 25 | }); 26 | 27 | return array_slice($news, 0, $limit); 28 | } 29 | 30 | public static function getList() 31 | { 32 | return [ 33 | (object) [ 34 | 'id' => 1, 35 | 'title' => 'Cows lose their jobs as milk prices drop', 36 | 'category' => '', 37 | ], 38 | (object) [ 39 | 'id' => 2, 40 | 'title' => 'Man Accused of Killing Lawyer Receives a New Attorney', 41 | 'category' => '', 42 | ], 43 | (object) [ 44 | 'id' => 3, 45 | 'title' => 'State population to double by 2040, babies to blame', 46 | 'category' => '', 47 | ], 48 | (object) [ 49 | 'id' => 4, 50 | 'title' => 'Breathing oxygen linked to staying alive', 51 | 'category' => '', 52 | ], 53 | (object) [ 54 | 'id' => 5, 55 | 'title' => 'Most Earthquake Damage is Caused by Shaking', 56 | 'category' => '', 57 | ], 58 | (object) [ 59 | 'id' => 6, 60 | 'title' => 'Federal Agents Raid Gun Shop, Find Weapons', 61 | 'category' => '', 62 | ], 63 | (object) [ 64 | 'id' => 7, 65 | 'title' => 'Safety meeting ends in accident', 66 | 'category' => '', 67 | ], 68 | (object) [ 69 | 'id' => 8, 70 | 'title' => 'Utah Poison Control Center reminds everyone not to take poison', 71 | 'category' => '', 72 | ], 73 | (object) [ 74 | 'id' => 9, 75 | 'title' => 'Hospitals resort to hiring doctors', 76 | 'category' => '', 77 | ], 78 | (object) [ 79 | 'id' => 10, 80 | 'title' => 'Bugs flying around with wings are flying bugs', 81 | 'category' => '', 82 | ], 83 | ]; 84 | } 85 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Module.php: -------------------------------------------------------------------------------- 1 | name = $name; 34 | $this->vendor = $vendor ?? static::DEFAULT_VENDOR; 35 | } 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function getName(): string 41 | { 42 | return $this->name; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getVendor(): string 49 | { 50 | return $this->vendor; 51 | } 52 | 53 | public function getConfigPath() 54 | { 55 | return $this->getSourcePath().'/config'; 56 | } 57 | 58 | public function getSourcePath() 59 | { 60 | $path = PROJECT_ROOT.'/'; 61 | 62 | if ($this->vendor === static::DEFAULT_VENDOR) 63 | { 64 | $path .= 'bitrix/'.$this->name; 65 | } 66 | else 67 | { 68 | $path .= 'modules/'.$this->vendor.'.'.$this->name; 69 | } 70 | 71 | return $path; 72 | } 73 | 74 | public static function createFromString($module) 75 | { 76 | if (strpos($module, '.')) 77 | { 78 | list($vendor, $name) = explode('.', $module); 79 | } 80 | else 81 | { 82 | $name = $module; 83 | $vendor = static::DEFAULT_VENDOR; 84 | } 85 | 86 | return new static($name, $vendor); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /bitrix/main/lib/PackageLeadClassTrait.php: -------------------------------------------------------------------------------- 1 | getFileName()); 11 | } 12 | 13 | public function getProjectPath() 14 | { 15 | return str_replace(PROJECT_ROOT, '', $this->getPath()); 16 | } 17 | } -------------------------------------------------------------------------------- /bitrix/main/lib/QueueRequestHandler.php: -------------------------------------------------------------------------------- 1 | middleware[] = $middleware; 30 | } 31 | 32 | /** 33 | * @param ServerRequestInterface $request 34 | * @return ResponseInterface 35 | */ 36 | public function handle(ServerRequestInterface $request): ResponseInterface 37 | { 38 | if (0 === count($this->middleware)) 39 | { 40 | // last middleware in the queue has called on the request handler 41 | return $this->fallbackHandler->handle($request); 42 | } 43 | 44 | $middleware = array_shift($this->middleware); 45 | 46 | return $middleware->process($request, $this); 47 | } 48 | 49 | public function setFallbackHandler(RequestHandlerInterface $fallbackHandler): void 50 | { 51 | $this->fallbackHandler = $fallbackHandler; 52 | } 53 | 54 | public function getFallbackHandler() : ?RequestHandlerInterface 55 | { 56 | return $this->fallbackHandler; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /bitrix/main/lib/ResourceMeta.php: -------------------------------------------------------------------------------- 1 | hash] */ 21 | protected $map; 22 | 23 | public function __construct($path) 24 | { 25 | $this->path = $path; 26 | } 27 | 28 | public function getHash($resourcePath) 29 | { 30 | $hash = null; 31 | 32 | if (!empty($this->getMap()[strtolower($resourcePath)])) 33 | { 34 | $hash = $this->getMap()[strtolower($resourcePath)]; 35 | } 36 | 37 | return $hash; 38 | } 39 | 40 | public function getMap() 41 | { 42 | if ($this->map === null) 43 | { 44 | $lines = array_filter(file($this->path)); 45 | 46 | foreach ($lines as $line) 47 | { 48 | [$path, $hash] = explode(':', trim($line)); 49 | $this->map[strtolower($path)] = $hash; 50 | } 51 | } 52 | 53 | return $this->map; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /bitrix/main/lib/Response.php: -------------------------------------------------------------------------------- 1 | template = str_replace(PROJECT_ROOT.'/', '', $template); 35 | $this->template = str_replace('.twig', '', $this->template); // TODO replace to if_ends_with 36 | $this->context = $context; 37 | } 38 | 39 | public function render() 40 | { 41 | $twig = static::getTwigEnvironment(); 42 | 43 | return $twig->render($this->template.'.twig', $this->context); 44 | } 45 | 46 | public function __toString() 47 | { 48 | return $this->render(); 49 | } 50 | 51 | /** 52 | * TODO: Container? 53 | * @return \Twig\Environment 54 | */ 55 | public static function getTwigEnvironment() 56 | { 57 | if (static::$twig === null) 58 | { 59 | $loader = new \Twig\Loader\FilesystemLoader(PROJECT_ROOT); 60 | $twig = new \Twig\Environment($loader, [ 61 | 'debug' => true, 62 | 'cache' => new FilesystemCache(PROJECT_ROOT . '/cache/twig'), 63 | ]); 64 | 65 | $twig->addGlobal('route', Context::getRoute()); 66 | 67 | $twig->addExtension(new StackExtension()); 68 | $twig->addExtension(new BitrixExtension()); 69 | 70 | static::$twig = $twig; 71 | } 72 | 73 | return static::$twig; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/CompileCache.php: -------------------------------------------------------------------------------- 1 | initCache(3600*24*365, $cacheKey, $cacheDir)) 38 | { 39 | $cacheData = $cache->getVars(); 40 | } 41 | 42 | if (empty($cacheData)) 43 | { 44 | // compile all the routes 45 | $cacheData = []; 46 | 47 | foreach ($router->getRoutes() as $k => $route) 48 | { 49 | $cacheData[$k] = $route->getCompileCache(); 50 | } 51 | 52 | if ($cache->startDataCache()) 53 | { 54 | $cache->endDataCache($cacheData); 55 | } 56 | } 57 | else 58 | { 59 | // compile all the routes from cache 60 | foreach ($router->getRoutes() as $k => $route) 61 | { 62 | $route->compileFromCache($cacheData[$k]); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/ClosureController.php: -------------------------------------------------------------------------------- 1 | closure = $closure; 31 | $this->controllerFactory = $controllerFactory; 32 | } 33 | 34 | public function execute() 35 | { 36 | $container = Context::getContainer(); 37 | 38 | $routeParameters = $this->route 39 | ? $this->route->getParametersValues()->getValues() 40 | : []; 41 | 42 | // if signature with controller 43 | $reflection = new \ReflectionFunction($this->closure); 44 | 45 | foreach ($reflection->getParameters() as $reflectionParameter) 46 | { 47 | if (is_subclass_of($reflectionParameter->getType()?->getName(), \Bitrix\Main\Lib\Controllers\Controller::class)) 48 | { 49 | $controllerParameterName = $reflectionParameter->getName(); 50 | $controllerClass = $reflectionParameter->getType()->getName(); 51 | break; 52 | } 53 | } 54 | 55 | if (!empty($controllerParameterName) && !empty($controllerClass)) 56 | { 57 | // init controller 58 | $controller = $this->controllerFactory->create($controllerClass, route: $this->route); 59 | 60 | // set as parameter 61 | $routeParameters[$controllerParameterName] = $controller; 62 | } 63 | 64 | return $container->call($this->closure, $routeParameters); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | route; 28 | } 29 | 30 | /** 31 | * @param Route $route 32 | */ 33 | public function setRoute(Route $route): void 34 | { 35 | $this->route = $route; 36 | } 37 | 38 | abstract public function execute(); 39 | 40 | public function __invoke() 41 | { 42 | $result = $this->execute(); 43 | 44 | if (!($result instanceof Response)) 45 | { 46 | $result = new Response(200, [], $result); 47 | } 48 | 49 | return $result; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/EngineActionController.php: -------------------------------------------------------------------------------- 1 | actionClass = $actionClass; 31 | } 32 | 33 | public function execute() 34 | { 35 | require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php"); 36 | 37 | // actually action could be attached to a few controllers 38 | // but what if action was made for the one controller only 39 | // then it could be used in routing 40 | Loader::requireClass($this->actionClass); 41 | 42 | if (is_subclass_of($this->actionClass, Action::class)) 43 | { 44 | if (is_subclass_of($this->actionClass, RoutableAction::class)) 45 | { 46 | /** @var RoutableAction $actionClass */ 47 | $controllerClass = $this->actionClass::getControllerClass(); 48 | $actionName = $this->actionClass::getDefaultName(); 49 | 50 | /** @var \Bitrix\Main\Lib\HttpApplication $app */ 51 | $app = \Bitrix\Main\Lib\Application::getInstance(); 52 | $app->runController($controllerClass, $actionName); 53 | } 54 | else 55 | { 56 | throw new SystemException(sprintf( 57 | 'Action `%s` should implement %s interface for being called in routing', 58 | $this->actionClass, RoutableAction::class 59 | )); 60 | } 61 | } 62 | else 63 | { 64 | throw new SystemException(sprintf( 65 | 'Unknown controller `%s`', gettype($this->actionClass) 66 | )); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/EngineController.php: -------------------------------------------------------------------------------- 1 | controllerClass, $this->actionName] = $callback; 38 | 39 | $this->controllerFactory = $controllerFactory; 40 | } 41 | 42 | /** 43 | * @return \Bitrix\Main\Lib\Engine\Controller 44 | */ 45 | public function getControllerClass() 46 | { 47 | return $this->controllerClass; 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getActionName() 54 | { 55 | return $this->actionName; 56 | } 57 | 58 | public function execute() 59 | { 60 | $site = Context::getSite(); 61 | 62 | if (!empty($site)) 63 | { 64 | // run in context of site 65 | $template = $site->getBlankPagePath(); 66 | 67 | $context = [ 68 | 'controllerClass' => $this->controllerClass, 69 | 'controllerAction' => $this->actionName 70 | ]; 71 | 72 | return (new Response($template, $context))->render(); 73 | } 74 | else 75 | { 76 | $controller = $this->controllerFactory->create($this->controllerClass, route: $this->route); 77 | 78 | return Context::getContainer()->call( 79 | [$controller, $this->actionName], 80 | $this->route ? $this->route->getParametersValues()->getValues() : [] 81 | ); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/FallbackController.php: -------------------------------------------------------------------------------- 1 | closure = $closure; 19 | } 20 | 21 | public function __invoke() 22 | { 23 | $container = Context::getContainer(); 24 | 25 | $result = $container->call($this->closure); 26 | 27 | if (!($result instanceof Response)) 28 | { 29 | $result = new Response(status: 200, body: $result); 30 | } 31 | 32 | return $result; 33 | } 34 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/PublicPageController.php: -------------------------------------------------------------------------------- 1 | path = $path; 25 | } 26 | 27 | public function execute() 28 | { 29 | include_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/classes/general/virtual_io.php"); 30 | $io = CBXVirtualIo::GetInstance(); 31 | 32 | $_SERVER["REAL_FILE_PATH"] = $this->getPath(); 33 | Context::getCurrent()->getServer()->set('REAL_FILE_PATH', $this->getPath()); 34 | 35 | include_once($io->GetPhysicalName($_SERVER['DOCUMENT_ROOT'].$this->getPath())); 36 | die; 37 | } 38 | 39 | /** 40 | * @return mixed 41 | */ 42 | public function getPath() 43 | { 44 | return $this->path; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/RouteControllerFactory.php: -------------------------------------------------------------------------------- 1 | container = $container; 15 | } 16 | 17 | public function create(mixed $controllerDefinition): Controller 18 | { 19 | if ($controllerDefinition instanceof Controller) 20 | { 21 | $controller = $controllerDefinition; 22 | } 23 | elseif ($controllerDefinition instanceof \Closure) 24 | { 25 | $controller = $this->container->make(ClosureController::class, ['closure' => $controllerDefinition]); 26 | } 27 | elseif (is_array($controllerDefinition)) 28 | { 29 | $controller = $this->container->make(EngineController::class, ['callback' => $controllerDefinition]); 30 | } 31 | elseif (is_string($controllerDefinition)) 32 | { 33 | if (str_ends_with($controllerDefinition, '.twig')) 34 | { 35 | $controller = $this->container->make(TwigPageController::class, ['path' => $controllerDefinition]); 36 | } 37 | else 38 | { 39 | $controller = $this->container->make(EngineActionController::class, ['actionClass' => $controllerDefinition]); 40 | } 41 | } 42 | else 43 | { 44 | throw new ArgumentException(sprintf( 45 | 'Unknown route controller `%s`', gettype($controllerDefinition) 46 | )); 47 | } 48 | 49 | return $controller; 50 | } 51 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Controllers/TwigPageController.php: -------------------------------------------------------------------------------- 1 | pagePath = $path; 34 | } 35 | 36 | public function execute() 37 | { 38 | $site = Context::getSite(); 39 | $path = $site->getPagePath($this->pagePath); 40 | $response = new \Bitrix\Main\Lib\Response($path, []); 41 | 42 | return new Response(200, [], $response->render()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Exceptions/ParameterNotFoundException.php: -------------------------------------------------------------------------------- 1 | getPath() . '/config/routes'; 14 | $dirFiles = scandir($routesDir); 15 | $routeCallbacks = []; 16 | 17 | foreach ($dirFiles as $dirFile) 18 | { 19 | if (str_ends_with($dirFile, '.php')) 20 | { 21 | $routeCallbacks[] = include $routesDir . '/' . $dirFile; 22 | } 23 | } 24 | 25 | return $routeCallbacks; 26 | } 27 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/RouteMatchMiddleware.php: -------------------------------------------------------------------------------- 1 | router = $router; 34 | } 35 | 36 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface 37 | { 38 | /** @var QueueRequestHandler $handler */ 39 | $this->router->match($request, $handler); 40 | 41 | return $handler->handle($request); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/RouteRequestHandler.php: -------------------------------------------------------------------------------- 1 | route = $route; 22 | } 23 | 24 | public function handle(ServerRequestInterface $request) : ResponseInterface 25 | { 26 | // call the controller 27 | $controller = $this->route->getController(); 28 | 29 | return $controller(); 30 | } 31 | 32 | /** 33 | * @return Route 34 | */ 35 | public function getRoute(): Route 36 | { 37 | return $this->route; 38 | } 39 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/Router.php: -------------------------------------------------------------------------------- 1 | configurations[] = $configuration; 38 | } 39 | 40 | public function releaseRoutes() 41 | { 42 | // go recursively through routes tree 43 | $i = -1; 44 | while (isset($this->configurations[++$i])) 45 | { 46 | $this->routes = array_merge($this->routes, $this->configurations[$i]->release()); 47 | } 48 | 49 | // reindex 50 | $this->reindexRoutes(); 51 | 52 | // don't need them anymore 53 | $this->configurations = []; 54 | } 55 | 56 | protected function reindexRoutes() 57 | { 58 | $this->routesByName = []; 59 | 60 | foreach ($this->routes as $route) 61 | { 62 | if ($route->getOptions() && $route->getOptions()->hasName()) 63 | { 64 | $this->routesByName[$route->getOptions()->getFullName()] = $route; 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * @param ServerRequestInterface $request 71 | * 72 | * @param QueueRequestHandler $requestHandler TODO should be RequestHandlerInterface interface 73 | * 74 | * @return void 75 | */ 76 | public function match($request, $requestHandler) 77 | { 78 | $path = urldecode($request->getUri()->getPath()); 79 | 80 | foreach ($this->routes as $route) 81 | { 82 | if ($matchResult = $route->match($path, $request, $requestHandler)) 83 | { 84 | // check for routing tree match 85 | if ($requestHandler->getFallbackHandler() instanceof RouteRequestHandler) 86 | { 87 | break; 88 | } 89 | 90 | // check method 91 | if (!empty($route->getOptions()->getMethods()) 92 | && !in_array($request->getMethod(), $route->getOptions()->getMethods(), true)) 93 | { 94 | continue; 95 | } 96 | 97 | // set route parameters 98 | if (is_array($matchResult)) 99 | { 100 | $route->getParametersValues()->setValues($matchResult); 101 | } 102 | 103 | // set site 104 | if ($route->getOptions()->hasSite()) 105 | { 106 | $siteClass = $route->getOptions()->getSite(); 107 | $site = new $siteClass; 108 | 109 | // set layout 110 | if ($route->getOptions()->hasLayout()) 111 | { 112 | $site->setLayout($route->getOptions()->getLayout()); 113 | } 114 | 115 | Context::setSite($site); 116 | } 117 | 118 | // set current route 119 | Context::setRoute($route); 120 | 121 | //$fallbackHandler = new RouteRequestHandler($route); 122 | $fallbackHandler = new RoutingRequestHandler($route->getController()); 123 | $requestHandler->setFallbackHandler($fallbackHandler); 124 | 125 | // add route specific middleware 126 | if ($route->getOptions()->getMiddleware()) 127 | { 128 | foreach ($route->getOptions()->getMiddleware() as $middlewareClass) 129 | { 130 | $requestHandler->addMiddleware(new $middlewareClass); 131 | } 132 | } 133 | 134 | break; 135 | } 136 | } 137 | 138 | // not found 139 | if (empty($requestHandler->getFallbackHandler()) && isset($this->fallbackNotFound)) 140 | { 141 | $fallbackHandler = new RoutingRequestHandler($this->fallbackNotFound); 142 | $requestHandler->setFallbackHandler($fallbackHandler); 143 | } 144 | } 145 | 146 | public function url($url, $parameters = []) 147 | { 148 | // scheme, domain? 149 | $finalUrl = $url; 150 | 151 | if (!empty($parameters)) 152 | { 153 | $finalUrl .= '?'.http_build_query($parameters); 154 | } 155 | 156 | return $finalUrl; 157 | } 158 | 159 | public function route($name, $parameters = []) 160 | { 161 | if (!empty($this->routesByName[$name])) 162 | { 163 | // route should be compiled 164 | $route = $this->routesByName[$name]; 165 | $route->compile(); 166 | 167 | $uri = $route->getUri(); 168 | 169 | if (!empty($routeParameters = $route->getParameters())) 170 | { 171 | foreach ($routeParameters as $parameterName => $pattern) 172 | { 173 | if (array_key_exists($parameterName, $parameters)) 174 | { 175 | // get from user 176 | $value = $parameters[$parameterName]; 177 | 178 | // remove from user list 179 | unset($parameters[$parameterName]); 180 | } 181 | elseif ($route->getOptions() && $route->getOptions()->hasDefault($parameterName)) 182 | { 183 | $value = $route->getOptions()->getDefault($parameterName); 184 | } 185 | elseif ($route->getOptions() && $route->getOptions()->hasBoundParameter($parameterName)) 186 | { 187 | $value = $route->getOptions()->getBoundParameter($parameterName, $routeParameters); 188 | } 189 | else 190 | { 191 | throw new ParameterNotFoundException(sprintf( 192 | 'Parameter `%s` was not found', $parameterName 193 | )); 194 | } 195 | 196 | // check with pattern? 197 | 198 | $uri = str_replace("{{$parameterName}}", urlencode($value), $uri); 199 | } 200 | } 201 | 202 | // additional parameters as query string 203 | if (!empty($parameters)) 204 | { 205 | $uri .= '?'.http_build_query($parameters); 206 | } 207 | 208 | return $uri; 209 | } 210 | } 211 | 212 | /** 213 | * @return Route[] 214 | */ 215 | public function getRoutes() 216 | { 217 | return $this->routes; 218 | } 219 | 220 | public function hasRouteByName($routeName) 221 | { 222 | return !empty($this->routesByName[$routeName]); 223 | } 224 | 225 | public function setFallbackNotFound(Controller $controller) 226 | { 227 | $this->fallbackNotFound = $controller; 228 | } 229 | 230 | public function getFallbackNotFound() 231 | { 232 | return $this->fallbackNotFound; 233 | } 234 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/RoutingConfigurator.php: -------------------------------------------------------------------------------- 1 | scopeOptions = new Options; 52 | } 53 | 54 | public function __call($method, $arguments) 55 | { 56 | // setting option 57 | if (in_array($method, Options::$optionList, true)) 58 | { 59 | $configuration = $this->createConfiguration(); 60 | return $configuration->$method(...$arguments); 61 | } 62 | 63 | // setting route 64 | if (in_array($method, RoutingConfiguration::$configurationList, true)) 65 | { 66 | $configuration = $this->createConfiguration(); 67 | return $configuration->$method(...$arguments); 68 | } 69 | 70 | throw new SystemException(sprintf( 71 | 'Unknown method `%s` for object `%s`', $method, get_called_class() 72 | )); 73 | } 74 | 75 | public function createConfiguration() 76 | { 77 | $configuration = Context::getContainer()->make(RoutingConfiguration::class); 78 | 79 | $configuration->setConfigurator($this); 80 | $this->router->registerConfiguration($configuration); 81 | 82 | $configuration->setOptions(clone $this->scopeOptions); 83 | 84 | return $configuration; 85 | } 86 | 87 | public function mergeOptionsWith($anotherOptions) 88 | { 89 | $this->scopeOptions->mergeWith($anotherOptions); 90 | } 91 | 92 | public function fallbackNotFound($controller) 93 | { 94 | $controller = Context::getContainer()->get(RouteControllerFactory::class)->create($controller); 95 | $this->router->setFallbackNotFound($controller); 96 | } 97 | 98 | /** 99 | * @return Router 100 | */ 101 | public function getRouter() 102 | { 103 | return $this->router; 104 | } 105 | 106 | /** 107 | * @param Router $router 108 | */ 109 | public function setRouter($router) 110 | { 111 | $this->router = $router; 112 | } 113 | 114 | public function __clone() 115 | { 116 | $this->scopeOptions = clone $this->scopeOptions; 117 | $this->scopeOptions->clearCurrent(); 118 | } 119 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Routing/RoutingRequestHandler.php: -------------------------------------------------------------------------------- 1 | controller = $controller; 17 | } 18 | 19 | public function handle(ServerRequestInterface $request) : ResponseInterface 20 | { 21 | return ($this->controller)(); 22 | } 23 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Site.php: -------------------------------------------------------------------------------- 1 | */ 29 | protected $layout; 30 | 31 | public function getConfigPath() 32 | { 33 | return $this->getPath().'/config'; 34 | } 35 | 36 | public function getPagePath($pagePath) 37 | { 38 | $path = $this->getPath() . '/pages/' . $pagePath; 39 | 40 | if (!file_exists($path) && $this->getParent()) 41 | { 42 | $path = $this->getParent()->getPagePath($pagePath); 43 | } 44 | 45 | return $path; 46 | } 47 | 48 | public function getBlankPagePath() 49 | { 50 | return $this->getPagePath('blank.twig'); 51 | } 52 | 53 | /** 54 | * @return static 55 | */ 56 | public function getParent() 57 | { 58 | if ($this->parent === null) 59 | { 60 | $refClass = new \ReflectionClass($this); 61 | 62 | if ($refClass->getParentClass()->getName() !== __CLASS__) 63 | { 64 | $this->parent = $refClass->getParentClass()->newInstance(); 65 | } 66 | } 67 | 68 | return $this->parent; 69 | } 70 | 71 | public function getLayout() 72 | { 73 | return $this->layout; 74 | } 75 | 76 | public function setLayout($name) 77 | { 78 | $this->layout = $name; 79 | } 80 | 81 | /** 82 | * @internal 83 | * For internal usage in Twig. Adds layout context as global parameters 84 | * 85 | * @param class-string $layoutClass 86 | * @return string 87 | * @throws \DI\DependencyException 88 | * @throws \DI\NotFoundException 89 | */ 90 | public function loadLayout($layoutClass = null) 91 | { 92 | $layoutClass = $layoutClass ?: $this->layout; 93 | 94 | if (!isset($layoutClass)) 95 | { 96 | throw new SystemException(sprintf( 97 | 'Undefined layout for `%s` site', get_class($this) 98 | )); 99 | } 100 | 101 | /** @var Layout $layout */ 102 | $layout = Context::getContainer()->make($layoutClass); 103 | $globals = $layout->getData(); 104 | 105 | // load global parameters 106 | if (!empty($globals)) 107 | { 108 | $env = Response::getTwigEnvironment(); 109 | $ext = $env->getExtension(StackExtension::class); 110 | 111 | foreach ($globals as $key => $value) 112 | { 113 | $ext->addGlobal($key, $value); 114 | } 115 | } 116 | 117 | return $layout->getFileProjectPath(); 118 | } 119 | 120 | public function getRoutes() 121 | { 122 | $routeCallbacks = $this->baseGetRoutes(); 123 | 124 | // get parent 125 | if ($this->getParent()) 126 | { 127 | $routeCallbacks = array_merge($routeCallbacks, $this->getParent()->getRoutes()); 128 | } 129 | 130 | return $routeCallbacks; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /bitrix/main/lib/Solution.php: -------------------------------------------------------------------------------- 1 | getParametersMap(); 16 | 17 | if (isset($parametersMap[$controllerClass])) 18 | { 19 | $parameters = $parametersMap[$controllerClass]; 20 | 21 | foreach ($parameters as $parameterName => $parameterValue) 22 | { 23 | if ($parameterValue instanceof \Closure) 24 | { 25 | $parameterValue = $parameterValue(); 26 | } 27 | 28 | if ($parameterValue !== null) 29 | { 30 | $controller->{$parameterName} = $parameterValue; 31 | } 32 | } 33 | } 34 | } 35 | 36 | public function getParametersMap(): array 37 | { 38 | return []; 39 | } 40 | } -------------------------------------------------------------------------------- /bitrix/main/lib/StaticController.php: -------------------------------------------------------------------------------- 1 | getParameterValue('path'); 23 | $filePath = $path; 24 | 25 | // try to extract hash 26 | $lastDotPos = strrpos($path, '.'); 27 | $prevDotPos = strrpos(substr($path, 0, $lastDotPos), '.'); 28 | $hash = substr($path, $prevDotPos + 1, ($lastDotPos - $prevDotPos) - 1); 29 | $ext = substr($path, $lastDotPos + 1); 30 | 31 | if (preg_match('/^[a-f0-9]{12}$/', $hash)) 32 | { 33 | $filePath = substr($path, 0, $prevDotPos) . substr($path, $lastDotPos); 34 | } 35 | 36 | // TODO: check hash validity to prevent false positive copies of file 37 | 38 | $filePath = PROJECT_ROOT.'/'.$filePath; 39 | $fileContent = file_get_contents($filePath); 40 | 41 | if ($ext === 'css') 42 | { 43 | header('Content-Type: text/css'); 44 | 45 | // if css, then replace paths to pics 46 | // only for absolute paths, as long as relatives already works 47 | $fileContent = $this->replaceCssImagePaths($fileContent, $filePath); 48 | } 49 | 50 | // save public cache 51 | $publicPath = PROJECT_ROOT.'/public/resources/'.$path; 52 | $publicDir = dirname($publicPath); 53 | 54 | if (!file_exists($publicDir)) 55 | { 56 | mkdir($publicDir, 0777, true); 57 | } 58 | 59 | file_put_contents($publicPath, $fileContent); 60 | 61 | // output or internal redirect 62 | echo $fileContent; 63 | die; 64 | } 65 | 66 | protected function replaceCssImagePaths($cssContent, $cssFilePath) 67 | { 68 | $cssParser = new \Sabberworm\CSS\Parser($cssContent); 69 | $cssDocument = $cssParser->parse(); 70 | 71 | foreach($cssDocument->getAllValues() as $cssValue) 72 | { 73 | if($cssValue instanceof \Sabberworm\CSS\Value\URL) 74 | { 75 | $url = $cssValue->getURL()->getString(); 76 | 77 | // skip absolute urls with host 78 | if (strpos($url,'://') !== false) 79 | { 80 | continue; 81 | } 82 | 83 | // transform url 84 | $absPath = static::isRelativePath($url) 85 | ? dirname($cssFilePath).'/'.$url 86 | : PROJECT_ROOT.$url; 87 | 88 | $realPath = realpath($absPath); 89 | 90 | if ($realPath === false) 91 | { 92 | trigger_error(sprintf('File `%s` does not exist ', $absPath), E_USER_WARNING); 93 | } 94 | else 95 | { 96 | // file exists 97 | $hash = substr(md5_file($realPath), 0, 12); 98 | $relativePath = str_replace(PROJECT_ROOT.'/', '', $realPath); 99 | 100 | // 101 | $publicUrl = static::rewriteFileNameWithHash('/resources/'.$relativePath, $hash); 102 | $cssValue->getURL()->setString($publicUrl); 103 | 104 | // save to global map 105 | $map[$relativePath] = $hash; 106 | } 107 | } 108 | } 109 | 110 | return $cssDocument->render(); 111 | } 112 | 113 | protected static function isRelativePath($path) 114 | { 115 | if (strpos($path,'://') !== false) 116 | { 117 | return false; 118 | } 119 | elseif (substr($path,0,1) === '/') 120 | { 121 | return false; 122 | } 123 | else 124 | { 125 | // no protocol and no leading slash: relative 126 | return true; 127 | } 128 | } 129 | 130 | protected static function rewriteFileNameWithHash($path, $hash) 131 | { 132 | $resourcePathParts = explode('/', $path); 133 | 134 | $resourceFilename = basename($path); 135 | 136 | $lastDotPos = strrpos($resourceFilename, '.'); 137 | $resourceName = substr($resourceFilename, 0, $lastDotPos); 138 | $resourceExt = substr($resourceFilename, $lastDotPos + 1); 139 | 140 | $staticResourceFilename = $resourceName.'.'.$hash.'.'.$resourceExt; 141 | $resourcePathParts[count($resourcePathParts)-1] = $staticResourceFilename; 142 | 143 | // return uri with hash 144 | $path = join('/', $resourcePathParts); 145 | 146 | return $path; 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /bitrix/main/lib/Templates/Template.php: -------------------------------------------------------------------------------- 1 | router = new TemplateRouter($routerContext); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Templates/TemplateRouter.php: -------------------------------------------------------------------------------- 1 | context = $context; 15 | } 16 | 17 | public function linkTo($routeName, $routeParameters) 18 | { 19 | $currentRoute = Context::getRoute(); 20 | 21 | $allOptions = []; 22 | 23 | // custom routing configuration 24 | if (!empty($this->context['configuration'])) 25 | { 26 | $allOptions[] = $currentRoute->getCustomOptions($this->context['configuration']); 27 | } 28 | 29 | $allOptions[] = $currentRoute->getOptions(); 30 | 31 | // build fully qualified names 32 | $routeParentNames = $routeNames = $currentRoute->getOptions()->getParentNames(); 33 | $routeNames[] = $routeName; 34 | 35 | //$routeParentFqn = join(Options::COMPLEX_ROUTE_SEPARATOR, $routeParentNames); 36 | $routeFqn = join(Options::COMPLEX_ROUTE_SEPARATOR, $routeNames); 37 | 38 | $uri = null; 39 | 40 | foreach ($allOptions as $options) 41 | { 42 | // check for an alias 43 | if (!empty($options->getRouteBinding()[$routeName])) 44 | { 45 | return Context::getRouter()->route($options->getRouteBinding()[$routeName], $routeParameters); 46 | } 47 | 48 | // parameters with fqn 49 | // $parameters = []; 50 | // 51 | // foreach ($routeParameters as $localParameter => $parameterValue) 52 | // { 53 | // $parameterFqn = $routeParentFqn . Options::COMPLEX_ROUTE_SEPARATOR . $localParameter; 54 | // $parameters[$parameterFqn] = $parameterValue; 55 | // 56 | // // duplicate original keys if they are "absolute" in context of complex structure 57 | // $parameters[$localParameter] = $parameterValue; 58 | // } 59 | // $parameters = $routeParameters; 60 | 61 | // search for aliased solutions 62 | foreach ($options->getSolutionBinding() as $solutionAlias) 63 | { 64 | $routeAlias = $solutionAlias . Options::COMPLEX_ROUTE_SEPARATOR . $routeName; 65 | 66 | if (Context::getRouter()->hasRouteByName($routeAlias)) 67 | { 68 | $uri = Context::getRouter()->route($routeAlias, $routeParameters); 69 | break 2; 70 | } 71 | } 72 | 73 | if ($uri === null) 74 | { 75 | // final uri 76 | if (Context::getRouter()->hasRouteByName($routeFqn)) 77 | { 78 | // relative route 79 | $uri = Context::getRouter()->route($routeFqn, $routeParameters); 80 | break; 81 | } 82 | elseif (Context::getRouter()->hasRouteByName($routeName)) 83 | { 84 | // absolute route 85 | $uri = Context::getRouter()->route($routeName, $routeParameters); 86 | break; 87 | } 88 | } 89 | } 90 | 91 | if ($uri === null) 92 | { 93 | throw new \Exception('Route `'.$routeName.'` not found'); 94 | } 95 | 96 | return $uri; 97 | } 98 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Bitrix/Extension/BitrixExtension.php: -------------------------------------------------------------------------------- 1 | ['html']] 30 | ), 31 | new TwigFunction( 32 | 'resource', 33 | [$this, 'getResourcePath'] 34 | ), 35 | new TwigFunction( 36 | 'asset', 37 | [$this, 'includeAsset'], 38 | ['is_safe' => ['html']] 39 | ), 40 | new TwigFunction( 41 | 'extension', 42 | [$this, 'includeExtension'] 43 | ), 44 | ]; 45 | } 46 | 47 | /** 48 | * Returns the token parser instances to add to the existing list. 49 | * 50 | * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances 51 | */ 52 | public function getTokenParsers() 53 | { 54 | return [ 55 | new LayoutParser, 56 | ]; 57 | } 58 | 59 | /** 60 | * Returns the node visitor instances to add to the existing list. 61 | */ 62 | public function getNodeVisitors() 63 | { 64 | return [ 65 | new ExtendsNodeVisitor 66 | ]; 67 | } 68 | 69 | public function includeComponent(array $controllerAction, array $context = [], $configurationAlias = null) 70 | { 71 | [$controllerClass, $actionName] = $controllerAction; 72 | $container = Context::getContainer(); 73 | 74 | $controllerFactory = $container->get(ControllerFactory::class); 75 | $controller = $controllerFactory->create($controllerClass, configurationAlias: $configurationAlias); 76 | 77 | $route = Context::getRoute(); 78 | 79 | // parameters for this call 80 | foreach ($context as $parameterName => $parameterValue) 81 | { 82 | if (!property_exists($controller, $parameterName)) 83 | { 84 | throw new ArgumentException(sprintf( 85 | 'Controller `%s` has no property `%s`', get_class($controller), $parameterName 86 | )); 87 | } 88 | 89 | $controller->{$parameterName} = $parameterValue; 90 | } 91 | 92 | // call action 93 | return $container->call([$controller, $actionName], $route->getParametersValues()->getValues()); 94 | } 95 | 96 | public function getResourcePath($path) 97 | { 98 | Context::getAssetManager()->getResourcePath($path); 99 | } 100 | 101 | public function includeAsset($asset) 102 | { 103 | Context::getAssetManager()->addAsset($asset); 104 | } 105 | 106 | /** 107 | * @param Extension|string $extensionClass 108 | * @return mixed 109 | */ 110 | public function includeExtension($extensionClass) 111 | { 112 | /** @var Extension $extension */ 113 | $extension = new $extensionClass; 114 | 115 | foreach ($extension->getAssets() as $asset) 116 | { 117 | $this->includeAsset($asset); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Bitrix/Node/LayoutExpression.php: -------------------------------------------------------------------------------- 1 | raw('\\'. Context::class.'::getSite()->loadLayout(') 34 | ->repr($this->getAttribute('value')) 35 | ->raw(')') 36 | ; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Bitrix/NodeVisitor/ExtendsNodeVisitor.php: -------------------------------------------------------------------------------- 1 | hasNode('parent')) 37 | { 38 | $parentNode = $node->getNode('parent'); 39 | 40 | // layout has its own login 41 | if (!($parentNode instanceof LayoutExpression)) 42 | { 43 | $templateName = $parentNode->getAttribute('value'); 44 | 45 | if (strpos($templateName, '/') !== 0) 46 | { 47 | // relative path, add source template's path 48 | $sourceTemplate = $node->getSourceContext()->getName(); 49 | $sourceTemplatePath = dirname($sourceTemplate); 50 | 51 | $parentNode->setAttribute( 52 | 'value', 53 | $sourceTemplatePath.'/'.$templateName 54 | ); 55 | } 56 | } 57 | } 58 | 59 | // includes 60 | if ( 61 | $node instanceof FunctionExpression 62 | && $node->getAttribute('name') === 'include' 63 | ) 64 | { 65 | // get first argument of function - path of template to include 66 | $fileToIncludeNode = $node->getNode('arguments')->getNode('0'); 67 | 68 | if ($fileToIncludeNode instanceof ConstantExpression) 69 | { 70 | $templateName = $fileToIncludeNode->getAttribute('value'); 71 | 72 | if (strpos($templateName, '/') !== 0) 73 | { 74 | // relative path, add source template's path 75 | $sourceTemplate = $node->getSourceContext()->getName(); 76 | $sourceTemplatePath = dirname($sourceTemplate); 77 | 78 | $fileToIncludeNode->setAttribute( 79 | 'value', 80 | $sourceTemplatePath.'/'.$templateName 81 | ); 82 | } 83 | } 84 | } 85 | 86 | // resources 87 | if ( 88 | $node instanceof FunctionExpression 89 | && ( 90 | $node->getAttribute('name') === 'resource' 91 | || $node->getAttribute('name') === 'asset' 92 | ) 93 | ) 94 | { 95 | // get first argument of function - path of template to include 96 | $fileToIncludeNode = $node->getNode('arguments')->getNode('0'); 97 | 98 | if ($fileToIncludeNode instanceof ConstantExpression) 99 | { 100 | $templateName = $fileToIncludeNode->getAttribute('value'); 101 | 102 | if (strpos($templateName, '/') !== 0) 103 | { 104 | // relative path, add source template's path 105 | $sourceTemplate = $node->getSourceContext()->getName(); 106 | $sourceTemplatePath = dirname($sourceTemplate); 107 | 108 | $fileToIncludeNode->setAttribute( 109 | 'value', 110 | $sourceTemplatePath.'/'.$templateName 111 | ); 112 | } 113 | } 114 | } 115 | 116 | 117 | return $node; 118 | } 119 | 120 | /** 121 | * Called after child nodes are visited. 122 | * 123 | * @param Node $node The node to visit 124 | * @param Environment $env The Twig environment instance 125 | * @return Node|false The modified node or false if the node must be removed 126 | */ 127 | public function leaveNode(Node $node, Environment $env): ?Node 128 | { 129 | return $node; 130 | } 131 | 132 | /** 133 | * Returns the priority for this visitor. 134 | * Priority should be between -10 and 10 (0 is the default). 135 | * 136 | * @return integer The priority level 137 | */ 138 | public function getPriority() 139 | { 140 | return 5; 141 | } 142 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Bitrix/TokenParser/LayoutParser.php: -------------------------------------------------------------------------------- 1 | parser->getStream(); 27 | 28 | if ($this->parser->peekBlockStack()) { 29 | throw new SyntaxError('Cannot use "layout" in a block.', $token->getLine(), $stream->getSourceContext()); 30 | } elseif (!$this->parser->isMainScope()) { 31 | throw new SyntaxError('Cannot use "layout" in a macro.', $token->getLine(), $stream->getSourceContext()); 32 | } 33 | 34 | if (null !== $this->parser->getParent()) { 35 | throw new SyntaxError('Multiple extends tags are forbidden.', $token->getLine(), $stream->getSourceContext()); 36 | } 37 | 38 | if ($stream->test(/* Token::BLOCK_END_TYPE */ 3)) 39 | { 40 | $value = null; 41 | } else { 42 | $parent = $this->parser->getExpressionParser()->parseExpression(); 43 | $value = $parent->getAttribute('value'); 44 | } 45 | 46 | $parent = new LayoutExpression($value, $token->getLine()); 47 | 48 | $this->parser->setParent($parent); 49 | 50 | 51 | 52 | $stream->expect(/* Token::BLOCK_END_TYPE */ 3); 53 | 54 | return new Node(); 55 | } 56 | 57 | public function getTag(): string 58 | { 59 | return 'layout'; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/FileCache.php: -------------------------------------------------------------------------------- 1 | env = $env; 28 | 29 | // duplicate private property 30 | $this->directory = $directory; 31 | } 32 | 33 | public function generateKey(string $name, string $className): string 34 | { 35 | $fullPath = $this->env->getLoader()->getCacheKey($name); 36 | $relativePath = $fullPath; 37 | 38 | $projectRoot = Context::getCurrent()->getServer()->getDocumentRoot(); 39 | if (strpos($fullPath, $projectRoot) === 0) 40 | { 41 | $relativePath = substr($fullPath, strlen($projectRoot)); 42 | } 43 | 44 | return $this->directory.'/'.$relativePath.'.php'; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Extension/StackExtension.php: -------------------------------------------------------------------------------- 1 | deferredStack)) 56 | { 57 | $this->deferredStack[$stackName] = new Stack; 58 | } 59 | 60 | $this->deferredStack[$stackName]->push($content, $prepend); 61 | } 62 | 63 | /** 64 | * Returns the token parser instances to add to the existing list. 65 | * 66 | * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances 67 | */ 68 | public function getTokenParsers() 69 | { 70 | return [ 71 | new PushTokenParser 72 | ]; 73 | } 74 | 75 | /** 76 | * Returns the node visitor instances to add to the existing list. 77 | * 78 | * @return Node[] An array of Twig_NodeVisitorInterface instances 79 | */ 80 | public function getNodeVisitors() 81 | { 82 | return [ 83 | new BufferedBodyNodeVisitor, 84 | new BufferedOutputNodeVisitor, 85 | new StackNodeVisitor, 86 | ]; 87 | } 88 | 89 | public function getFunctions() 90 | { 91 | return [ 92 | new TwigFunction(static::STACK_SHOW_FUNCTION_NAME, [$this, 'showStackContent'], ['is_safe' => ['all']]), 93 | new TwigFunction(static::STACK_GET_FUNCTION_NAME, [$this, 'getStackContent'], ['is_safe' => ['all']]) 94 | ]; 95 | } 96 | 97 | public function showStackContent($name) 98 | { 99 | $this->outputStack[] = new StackPointer($name); 100 | } 101 | 102 | public function getStackContent($name) 103 | { 104 | return isset($this->deferredStack[$name]) ? (string) $this->deferredStack[$name] : ''; 105 | } 106 | 107 | public function output($str) 108 | { 109 | $this->outputStack[] = $str; 110 | } 111 | 112 | /** 113 | * @param string $name 114 | * @param mixed $value 115 | */ 116 | public function addGlobal($name, $value) 117 | { 118 | $this->globals[$name] = $value; 119 | } 120 | 121 | /** 122 | * @param array $context 123 | * @return array 124 | */ 125 | public function mergeGlobals($context) 126 | { 127 | foreach ($this->globals as $key => $value) 128 | { 129 | if (!\array_key_exists($key, $context)) 130 | { 131 | $context[$key] = $value; 132 | } 133 | } 134 | 135 | return $context; 136 | } 137 | 138 | public function startRender() 139 | { 140 | ++$this->renderRequestLevel; 141 | } 142 | 143 | public function endRender() 144 | { 145 | if ($this->renderRequestLevel === 0) 146 | { 147 | // it is time to echo 148 | 149 | // final call event 150 | Context::getEventManager()->send(new Event('main', 'onPageRender', [ 151 | self::class => $this 152 | ])); 153 | 154 | // build output 155 | $this->renderStack(); 156 | 157 | // render stack 158 | foreach ($this->outputStack as $k => $item) 159 | { 160 | if ($item instanceof Response) 161 | { 162 | $this->outputStack[$k] = $item->render(); 163 | } 164 | } 165 | 166 | // output 167 | echo join('', $this->outputStack); 168 | } 169 | else 170 | { 171 | --$this->renderRequestLevel; 172 | } 173 | } 174 | 175 | protected function renderStack() 176 | { 177 | $deferredStack = $this->deferredStack; 178 | 179 | $this->outputStack = array_map(function ($elem) use ($deferredStack) { 180 | if ($elem instanceof StackPointer) 181 | { 182 | if (isset($deferredStack[(string) $elem])) 183 | { 184 | return (string) $deferredStack[(string) $elem]; 185 | } 186 | else 187 | { 188 | return ''; 189 | } 190 | } 191 | elseif (is_array($elem)) 192 | { 193 | $res = call_user_func($elem); 194 | return $res; 195 | } 196 | else 197 | { 198 | return $elem; 199 | } 200 | }, $this->outputStack); 201 | } 202 | 203 | public function reset() 204 | { 205 | $this->deferredStack = $this->outputStack = []; 206 | $this->renderRequestLevel = -1; 207 | } 208 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/BufferedBodyNode.php: -------------------------------------------------------------------------------- 1 | moduleNode = $moduleNode; 31 | 32 | parent::__construct(array('body' => $body)); 33 | } 34 | 35 | public function compile(Compiler $compiler): void 36 | { 37 | $compiler->write("\n", '$context = $this->extensions["'.StackExtension::class."\"]->mergeGlobals(\$context);\n\n"); 38 | $compiler->write("\n", '$this->extensions["'.StackExtension::class."\"]->startRender();\n\n"); 39 | parent::compile($compiler); 40 | $compiler->write("\n", '$this->extensions["'.StackExtension::class."\"]->endRender();\n"); 41 | } 42 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/BufferedConcatNode.php: -------------------------------------------------------------------------------- 1 | $body]); 27 | 28 | if ($concatVarName !== null) 29 | { 30 | $this->concatVarName = $concatVarName; 31 | } 32 | 33 | $this->setConcatOutputMode($this); 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | public function getConcatVarName() 40 | { 41 | return $this->concatVarName; 42 | } 43 | 44 | public function compile(Compiler $compiler) 45 | { 46 | $compiler 47 | ->write("\${$this->concatVarName} = \"\";\n") 48 | ; 49 | 50 | $compiler->subcompile($this->getNode('body')); 51 | } 52 | 53 | public function setConcatOutputMode(Node $node) 54 | { 55 | foreach ($node as $subNode) 56 | { 57 | if ($subNode instanceof BufferedNode) 58 | { 59 | $subNode->setOutputMode(BufferedNode::OUTPUT_MODE_CONCAT); 60 | $subNode->setConcatVarName($this->concatVarName); 61 | } 62 | elseif (count($subNode)) 63 | { 64 | $this->setConcatOutputMode($subNode); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/BufferedNode.php: -------------------------------------------------------------------------------- 1 | $node]); 34 | } 35 | 36 | /** 37 | * @param int $outputMode 38 | */ 39 | public function setOutputMode($outputMode): void 40 | { 41 | $this->outputMode = $outputMode; 42 | } 43 | 44 | /** 45 | * @param string $concatVarName 46 | */ 47 | public function setConcatVarName($concatVarName): void 48 | { 49 | $this->concatVarName = $concatVarName; 50 | } 51 | 52 | public function compile(Compiler $compiler) 53 | { 54 | $compiler->addDebugInfo($this->getNode('source_node')); 55 | 56 | switch ($this->outputMode) 57 | { 58 | case self::OUTPUT_MODE_ECHO: 59 | $this->compileEcho($compiler); 60 | break; 61 | 62 | case self::OUTPUT_MODE_BUFFERED: 63 | $this->compileBuffered($compiler); 64 | break; 65 | 66 | case self::OUTPUT_MODE_CONCAT: 67 | $this->compileConcat($compiler); 68 | break; 69 | 70 | case self::OUTPUT_MODE_RAW: 71 | $this->compileRaw($compiler); 72 | break; 73 | } 74 | } 75 | 76 | public function compileEcho(Compiler $compiler): void 77 | { 78 | $compiler 79 | ->subcompile($this->getNode('source_node')) 80 | ; 81 | } 82 | 83 | public function compileBuffered(Compiler $compiler): void 84 | { 85 | $compiler->write('$this->extensions["'.StackExtension::class.'"]->output('); 86 | $this->compileContent($compiler); 87 | $compiler->raw(");\n"); 88 | } 89 | 90 | public function compileConcat(Compiler $compiler): void 91 | { 92 | $compiler->write('$'.$this->concatVarName.' .= '); 93 | $this->compileContent($compiler); 94 | $compiler->raw(";\n"); 95 | } 96 | 97 | public function compileRaw(Compiler $compiler): void 98 | { 99 | $compiler->write(''); 100 | $this->compileContent($compiler); 101 | $compiler->raw(";\n\n"); 102 | } 103 | 104 | public function compileContent(Compiler $compiler) 105 | { 106 | $compiler 107 | ->subcompile($this->getNode('source_node')) 108 | ; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/BufferedPrintNode.php: -------------------------------------------------------------------------------- 1 | subcompile($this->getNode('source_node')->getNode('expr')) 29 | ; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/BufferedTextNode.php: -------------------------------------------------------------------------------- 1 | string($this->getNode('source_node')->getAttribute('data')) 29 | ; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/DeferredBodyNode.php: -------------------------------------------------------------------------------- 1 | $body], ['name' => $name]); 24 | } 25 | 26 | public function compile(Compiler $compiler): void 27 | { 28 | /** @var BufferedConcatNode $body */ 29 | $body = $this->getNode('body'); 30 | 31 | $compiler 32 | ->write(sprintf("public function %s()\n", $this->getAttribute('name')), "{\n") 33 | ->indent() 34 | ->subcompile($body) 35 | ->write('return $'.$body->getConcatVarName().";\n") 36 | ->outdent() 37 | ->write("}\n\n") 38 | ; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/DeferredNode.php: -------------------------------------------------------------------------------- 1 | moduleNode = $moduleNode; 31 | parent::__construct(['source_node' => $node]); 32 | } 33 | 34 | public function compile(Compiler $compiler) 35 | { 36 | $uniqName = "stack_".uniqid(); 37 | 38 | $compiler 39 | ->write('$this->extensions["'.StackExtension::class.'"]->output(') 40 | ->raw('[$this, "'.$uniqName.'"]') 41 | ->raw(");\n") 42 | ; 43 | 44 | $this->moduleNode->getNode('class_end')->setNode( 45 | $uniqName, new DeferredBodyNode($uniqName, $this->getNode('source_node')) 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/PushBodyNode.php: -------------------------------------------------------------------------------- 1 | $body], [], $lineno, $tag); 25 | } 26 | 27 | public function compile(Compiler $compiler): void 28 | { 29 | $this->compileNode($this, $compiler); 30 | 31 | $compiler->raw('""'); 32 | } 33 | 34 | public function compileNode(Node $node, Compiler $compiler) 35 | { 36 | foreach ($node->nodes as $subNode) 37 | { 38 | /** @var $subNode Node */ 39 | if ($subNode instanceof PrintNode) 40 | { 41 | $compiler 42 | ->addDebugInfo($subNode) 43 | ->subcompile($subNode->getNode('expr')) 44 | ->raw(".\n") 45 | ; 46 | } 47 | elseif ($subNode instanceof TextNode) 48 | { 49 | $compiler 50 | ->addDebugInfo($subNode) 51 | ->string($subNode->getAttribute('data')) 52 | ->raw(".\n") 53 | ; 54 | } 55 | else 56 | { 57 | if (!empty($subNode->nodes)) 58 | { 59 | $this->compileNode($subNode, $compiler); 60 | } 61 | else 62 | { 63 | $subNode->compile($compiler); 64 | } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Node/PushNode.php: -------------------------------------------------------------------------------- 1 | $body], ['name' => $name], $lineno, $tag); 24 | } 25 | 26 | public function compile(Compiler $compiler): void 27 | { 28 | /** @var BufferedConcatNode $body */ 29 | $body = $this->getNode('body'); 30 | 31 | $compiler 32 | ->addDebugInfo($this) 33 | ->subcompile($body) 34 | ->write(sprintf( 35 | "\$this->extensions['".StackExtension::class."']->pushStack('%s', ",// '%s');\n\n", 36 | $this->getAttribute('name') 37 | )) 38 | ->raw("\${$body->getConcatVarName()});\n") 39 | ; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/NodeVisitor/BufferedBodyNodeVisitor.php: -------------------------------------------------------------------------------- 1 | handleModuleNode($node); 44 | } 45 | 46 | return $node; 47 | } 48 | 49 | private function handleModuleNode(ModuleNode $node) 50 | { 51 | if ($node->hasNode('body') && !$node->hasNode('parent')) { 52 | $body = $node->getNode('body'); 53 | $node->setNode('body', new BufferedBodyNode($body, $node)); 54 | } 55 | } 56 | 57 | /** 58 | * Returns the priority for this visitor. 59 | * Priority should be between -10 and 10 (0 is the default). 60 | * 61 | * @return integer The priority level 62 | */ 63 | public function getPriority() 64 | { 65 | return 0; 66 | } 67 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/NodeVisitor/BufferedOutputNodeVisitor.php: -------------------------------------------------------------------------------- 1 | moduleNode = $node; 43 | } 44 | elseif ($node instanceof PushNode) 45 | { 46 | // wrap content with concat 47 | $body = $node->getNode('body'); 48 | 49 | $node->setNode('body', 50 | new BufferedConcatNode($body, 'out_'.uniqid()) 51 | ); 52 | } 53 | 54 | return $node; 55 | } 56 | 57 | /** 58 | * Called after child nodes are visited. 59 | * 60 | * @param Node $node The node to visit 61 | * @param Environment $env The Twig environment instance 62 | * @return Node|false The modified node or false if the node must be removed 63 | */ 64 | public function leaveNode(Node $node, Environment $env): ?Node 65 | { 66 | if ($node instanceof BufferedPrintNode) 67 | { 68 | $expr = $node->getNode('source_node')->getNode('expr'); 69 | 70 | if ($expr instanceof FunctionExpression 71 | && $expr->getAttribute('name') === StackExtension::STACK_SHOW_FUNCTION_NAME) 72 | { 73 | // no output for showing stack 74 | $node->setOutputMode(BufferedNode::OUTPUT_MODE_RAW); 75 | } 76 | elseif ($expr instanceof FilterExpression && $this->searchForStackFunctionAndReplace($expr)) 77 | { 78 | // filter on stack should be deferred 79 | $node = new DeferredNode($node, $this->moduleNode); 80 | } 81 | } 82 | elseif ($node instanceof IfNode && $this->searchForStackFunctionAndReplace($node->getNode('tests'))) 83 | { 84 | $node = new DeferredNode($node, $this->moduleNode); 85 | } 86 | 87 | return $node; 88 | } 89 | 90 | /** 91 | * Returns the priority for this visitor. 92 | * Priority should be between -10 and 10 (0 is the default). 93 | * 94 | * @return integer The priority level 95 | */ 96 | public function getPriority() 97 | { 98 | return 5; 99 | } 100 | 101 | protected function searchForStackFunctionAndReplace(Node $node) 102 | { 103 | $found = false; 104 | 105 | foreach ($node as $subNode) 106 | { 107 | if ($subNode instanceof FunctionExpression 108 | && $subNode->getAttribute('name') === StackExtension::STACK_SHOW_FUNCTION_NAME) 109 | { 110 | $subNode->setAttribute('name', StackExtension::STACK_GET_FUNCTION_NAME); 111 | $found = true; 112 | } 113 | 114 | if (count($subNode)) 115 | { 116 | $search = $this->searchForStackFunctionAndReplace($subNode); 117 | 118 | if ($search) 119 | { 120 | $found = true; 121 | } 122 | } 123 | } 124 | 125 | return $found; 126 | } 127 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/Stack.php: -------------------------------------------------------------------------------- 1 | prepend($content) 20 | : $this->append($content); 21 | } 22 | 23 | public function prepend($value) 24 | { 25 | $array = $this->getArrayCopy(); 26 | array_unshift($array, $value); 27 | $this->exchangeArray($array); 28 | } 29 | 30 | public function setSeparator($separator) 31 | { 32 | $this->separator = $separator; 33 | } 34 | 35 | public function __toString() 36 | { 37 | return join($this->separator, $this->getArrayCopy()); 38 | } 39 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/StackPointer.php: -------------------------------------------------------------------------------- 1 | name = $name; 24 | } 25 | 26 | 27 | public function __toString() 28 | { 29 | return $this->name; 30 | } 31 | } -------------------------------------------------------------------------------- /bitrix/main/lib/Twig/Stack/TokenParser/PushTokenParser.php: -------------------------------------------------------------------------------- 1 | getLine(); 26 | $stream = $this->parser->getStream(); 27 | $name = $stream->expect(Token::NAME_TYPE)->getValue(); 28 | 29 | $this->parser->pushLocalScope(); 30 | 31 | if ($stream->nextIf(Token::BLOCK_END_TYPE)) { 32 | $body = $this->parser->subparse([$this, 'decideBlockEnd'], true); 33 | if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) { 34 | $value = $token->getValue(); 35 | 36 | if ($value != $name) { 37 | throw new SyntaxError(sprintf('Expected endpush for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()); 38 | } 39 | } 40 | } else { 41 | $body = $this->parser->getExpressionParser()->parseExpression(); 42 | } 43 | $stream->expect(Token::BLOCK_END_TYPE); 44 | 45 | $this->parser->popLocalScope(); 46 | 47 | return new PushNode($name, $body, $lineno, $this->getTag()); 48 | } 49 | 50 | public function decideBlockEnd(Token $token): bool 51 | { 52 | return $token->test('endpush'); 53 | } 54 | 55 | public function getTag(): string 56 | { 57 | return 'push'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bitrix/main/lib/Type/ArrayHelper.php: -------------------------------------------------------------------------------- 1 | 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bitrix/main/lib/Type/Dictionary.php: -------------------------------------------------------------------------------- 1 | values = $values; 22 | } 23 | } 24 | 25 | /** 26 | * Returns any variable by its name. Null if variable is not set. 27 | * 28 | * @param string $name 29 | * @return string | array | null 30 | */ 31 | public function get($name) 32 | { 33 | // this condition a bit faster 34 | // it is possible to omit array_key_exists here, but for uniformity... 35 | if (isset($this->values[$name]) || array_key_exists($name, $this->values)) 36 | { 37 | return $this->values[$name]; 38 | } 39 | 40 | return null; 41 | } 42 | 43 | public function set($name, $value = null) 44 | { 45 | if (is_array($name)) 46 | { 47 | $this->values = $name; 48 | } 49 | else 50 | { 51 | $this->values[$name] = $value; 52 | } 53 | } 54 | 55 | /** 56 | * @return array 57 | */ 58 | public function getValues() 59 | { 60 | return $this->values; 61 | } 62 | 63 | /** 64 | * @param $values 65 | */ 66 | public function setValues($values) 67 | { 68 | $this->values = $values; 69 | } 70 | 71 | public function clear() 72 | { 73 | $this->values = array(); 74 | } 75 | 76 | /** 77 | * Return the current element 78 | */ 79 | public function current(): mixed 80 | { 81 | return current($this->values); 82 | } 83 | 84 | /** 85 | * Move forward to next element 86 | */ 87 | public function next(): void 88 | { 89 | next($this->values); 90 | } 91 | 92 | /** 93 | * Return the key of the current element 94 | */ 95 | public function key(): mixed 96 | { 97 | return key($this->values); 98 | } 99 | 100 | /** 101 | * Checks if current position is valid 102 | */ 103 | public function valid(): bool 104 | { 105 | return ($this->key() !== null); 106 | } 107 | 108 | /** 109 | * Rewind the Iterator to the first element 110 | */ 111 | public function rewind(): void 112 | { 113 | reset($this->values); 114 | } 115 | 116 | /** 117 | * Whether a offset exists 118 | */ 119 | public function offsetExists($offset): bool 120 | { 121 | return isset($this->values[$offset]) || array_key_exists($offset, $this->values); 122 | } 123 | 124 | /** 125 | * Offset to retrieve 126 | */ 127 | public function offsetGet($offset): mixed 128 | { 129 | if (isset($this->values[$offset]) || array_key_exists($offset, $this->values)) 130 | { 131 | return $this->values[$offset]; 132 | } 133 | 134 | return null; 135 | } 136 | 137 | /** 138 | * Offset to set 139 | */ 140 | public function offsetSet($offset, $value): void 141 | { 142 | if($offset === null) 143 | { 144 | $this->values[] = $value; 145 | } 146 | else 147 | { 148 | $this->values[$offset] = $value; 149 | } 150 | } 151 | 152 | /** 153 | * Offset to unset 154 | */ 155 | public function offsetUnset($offset): void 156 | { 157 | unset($this->values[$offset]); 158 | } 159 | 160 | /** 161 | * Count elements of an object 162 | */ 163 | public function count(): int 164 | { 165 | return count($this->values); 166 | } 167 | 168 | /** 169 | * Returns the values as an array. 170 | * 171 | * @return array 172 | */ 173 | public function toArray() 174 | { 175 | return $this->values; 176 | } 177 | 178 | /** 179 | * Returns true if the dictionary is empty. 180 | * @return bool 181 | */ 182 | public function isEmpty() 183 | { 184 | return empty($this->values); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /bitrix/main/lib/Type/ParameterDictionary.php: -------------------------------------------------------------------------------- 1 | arRawValues === null) 17 | $this->arRawValues = $this->values; 18 | $this->values = $values; 19 | } 20 | 21 | /** 22 | * Returns original value of any variable by its name. Null if variable is not set. 23 | * 24 | * @param string $name 25 | * @return string | null 26 | */ 27 | public function getRaw($name) 28 | { 29 | if ($this->arRawValues === null) 30 | { 31 | if (isset($this->values[$name]) || array_key_exists($name, $this->values)) 32 | return $this->values[$name]; 33 | } 34 | else 35 | { 36 | if (isset($this->arRawValues[$name]) || array_key_exists($name, $this->arRawValues)) 37 | return $this->arRawValues[$name]; 38 | } 39 | 40 | return null; 41 | } 42 | 43 | public function toArrayRaw() 44 | { 45 | return $this->arRawValues; 46 | } 47 | 48 | /** 49 | * Offset to set 50 | */ 51 | public function offsetSet($offset, $value): void 52 | { 53 | throw new NotSupportedException("Can not set readonly value"); 54 | } 55 | 56 | /** 57 | * Offset to unset 58 | */ 59 | public function offsetUnset($offset): void 60 | { 61 | throw new NotSupportedException("Can not unset readonly value"); 62 | } 63 | } -------------------------------------------------------------------------------- /bitrix/main/meta/resource.map.php: -------------------------------------------------------------------------------- 1 | '1ce16', 5 | 'components/NewsFeed/templates/default' => '2107ab', 6 | 'components/NewsDetailed/templates/default' => '4e05f3' 7 | ]; -------------------------------------------------------------------------------- /bitrix/main/sites/Supershop/SupershopSite.php: -------------------------------------------------------------------------------- 1 | prefix('shopforum') 9 | ->solution(\Bitrix\Main\Solutions\Forum\ForumSolution::class); 10 | 11 | $routes 12 | ->prefix('users') 13 | ->solution(\Bitrix\Main\Solutions\Users\UsersSolution::class); 14 | 15 | $routes 16 | ->get('/about', 'about.twig'); 17 | 18 | }; -------------------------------------------------------------------------------- /bitrix/main/sites/Supershop/pages/about.twig: -------------------------------------------------------------------------------- 1 | {% layout %} 2 | 3 | {% block workarea %} 4 | supershop about page 5 | {% endblock %} -------------------------------------------------------------------------------- /bitrix/main/sites/Supershop/pages/company.twig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/sites/Supershop/pages/company.twig -------------------------------------------------------------------------------- /bitrix/main/sites/Supershop/pages/index.twig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/sites/Supershop/pages/index.twig -------------------------------------------------------------------------------- /bitrix/main/sites/Supershop/pages/sales.twig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/bitrix/main/sites/Supershop/pages/sales.twig -------------------------------------------------------------------------------- /bitrix/main/sites/Supershop/routes/routes.php: -------------------------------------------------------------------------------- 1 | allowSmiles = function () { 20 | 21 | }; 22 | } 23 | 24 | 25 | public function getParametersMap(): array 26 | { 27 | return [ 28 | \Bitrix\Main\Components\DemoForumSection\Controller::class => [ 29 | 30 | ], 31 | \Bitrix\Main\Components\DemoForumThread\Controller::class => [ 32 | 'avatarSize' => $this->avatarSize, 33 | 'perPage' => $this->messagesPerPage, 34 | 'allowSmiles' => function () { 35 | // get option from db 36 | $this; 37 | } 38 | ], 39 | \Bitrix\Main\Components\DemoForum\Controller::class => [ 40 | 'showSectionDescription' => $this->showSectionDescriptionOnMainPage 41 | ] 42 | ]; 43 | } 44 | } 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /bitrix/main/solutions/Forum/config/routes/web.php: -------------------------------------------------------------------------------- 1 | get('/', [ 8 | \Bitrix\Main\Components\DemoForum\Controller::class, 9 | 'mainAction', 10 | ])->name('forum_main'); 11 | 12 | $routes->get('/{sectionId}', [ 13 | \Bitrix\Main\Components\DemoForumSection\Controller::class, 14 | 'mainAction', 15 | ])->name('forum_section'); 16 | 17 | $routes->get('/{sectionId}/thread/{threadId}', [ 18 | \Bitrix\Main\Components\DemoForumThread\Controller::class, 19 | 'mainAction', 20 | ]) 21 | ->name('forum_thread'); 22 | // ->bindParameter('threadId', function (ForumThread $thread) {return $thread->getId();}); 23 | 24 | $routes->get('/{sectionId}/thread/{threadId}/message/{messageId}', [ 25 | \Bitrix\Main\Components\DemoForumThread\Controller::class, 26 | 'messageAction', 27 | ])->name('forum_message'); 28 | }; -------------------------------------------------------------------------------- /bitrix/main/solutions/Users/UsersSolution.php: -------------------------------------------------------------------------------- 1 | get('/', [ 8 | \Bitrix\Main\Components\DemoUsers\Controller::class, 9 | 'listAction', 10 | ])->name('users_list'); 11 | 12 | $routes->get('/{id}', [ 13 | \Bitrix\Main\Components\DemoUser\Controller::class, 14 | 'mainAction', 15 | ])->name('users_profile'); 16 | }; -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "platform": { 4 | "php": "8.1" 5 | } 6 | }, 7 | "autoload": { 8 | "psr-4": { 9 | "Bitrix\\Main\\Lib\\": "bitrix/main/lib", 10 | "Bitrix\\Main\\Components\\": "bitrix/main/components", 11 | "Bitrix\\Main\\Solutions\\": "bitrix/main/solutions", 12 | "Bitrix\\Main\\Sites\\": "bitrix/main/sites", 13 | "Bitrix\\Main\\Layouts\\": "bitrix/main/layouts", 14 | "Sites\\": "sites" 15 | } 16 | }, 17 | "require": { 18 | "guzzlehttp/psr7": "^1.7", 19 | "psr/http-server-middleware": "^1.0", 20 | "php-di/php-di": "^6.3", 21 | "symfony/dependency-injection": "^5.1", 22 | "twig/twig": "^3.2", 23 | "sabberworm/php-css-parser": "^8.3", 24 | "twbs/bootstrap": "4.0.0", 25 | "psr/cache": "1.0.0", 26 | "ext-memcache": "*", 27 | "psr/log": "^3.0", 28 | "monolog/monolog": "^2.8", 29 | "filp/whoops": "^2.14" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | 'dev' 5 | ]; -------------------------------------------------------------------------------- /config/bootstrap.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'adapter' => \Bitrix\Main\Lib\Cache\Adapters\FilesystemAdapter::class, 6 | 'options' => [ 7 | 'dir' => 'cache/managed' 8 | ] 9 | ] 10 | ]; -------------------------------------------------------------------------------- /config/container/base.php: -------------------------------------------------------------------------------- 1 | function (ContainerInterface $container) { 12 | $whoops = new Run; 13 | $whoops->allowQuit(false); 14 | $whoops->writeToOutput(false); 15 | $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler); 16 | 17 | return $whoops; 18 | }, 19 | 20 | LoggerInterface::class => function (ContainerInterface $container) { 21 | $logger = new Logger('name'); 22 | //$logger->pushHandler(new StreamHandler('/www/app.log', Logger::DEBUG)); 23 | $logger->pushHandler(new FirePHPHandler()); 24 | 25 | return $logger; 26 | } 27 | ]; 28 | -------------------------------------------------------------------------------- /config/controllers/forum.php: -------------------------------------------------------------------------------- 1 | perPage = 120; 13 | }, 14 | 15 | function (Bitrix\Main\Components\DemoForumThread\Controller $controller) 16 | { 17 | $controller->perPage = 120; 18 | }, 19 | 20 | function (\Bitrix\Main\Components\Feedback\Controller $controller) 21 | { 22 | $controller->showExpanded = true; 23 | } 24 | 25 | // #[RouteName('forum_thread', 'forum_section')] 26 | // #[ComponentAlias('clientForumComponent1', 'clientForumComponent2')] // AND or OR 27 | // function (Bitrix\Main\Components\DemoForumThread\Controller $controller) 28 | // { 29 | // $controller->perPage = 20; 30 | // }, 31 | 32 | ]; 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /config/db.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'default' => [ 6 | 'driver' => 'mysqli', 7 | 'username' => 'root', 8 | 'password' => 'pwsecret', 9 | ] 10 | ] 11 | ]; -------------------------------------------------------------------------------- /config/maintenance.php: -------------------------------------------------------------------------------- 1 | false, 5 | 'retry' => null, 6 | 'refresh' => null 7 | ]; -------------------------------------------------------------------------------- /config/middleware.php: -------------------------------------------------------------------------------- 1 | fallbackNotFound(function () { 8 | echo 'not found :('; 9 | }); // for a specific site? 10 | 11 | // for dev environment 12 | $routes->get( 13 | '/resources/{path}', 14 | [\Bitrix\Main\Lib\StaticController::class, 'resolveAction'] 15 | )->where('path', '.*'); 16 | 17 | $routes 18 | ->prefix('myshop') 19 | ->site(\Sites\s3\Site::class); 20 | }; -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix24/framework3-prototype/d53d119f32e218f38eea340845e230c93e5787aa/public/favicon.ico -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 19 | 20 | echo $response->getBody(); 21 | 22 | $kernel->terminate(); -------------------------------------------------------------------------------- /sites/s3/Site.php: -------------------------------------------------------------------------------- 1 | get('/about', function () { return 'custom about';}); 9 | 10 | }; -------------------------------------------------------------------------------- /sites/s3/pages/404.twig: -------------------------------------------------------------------------------- 1 | custom 404 -------------------------------------------------------------------------------- /sites/s3/pages/about.twig: -------------------------------------------------------------------------------- 1 | {% layout %} 2 | 3 | {% block workarea %} 4 | custom about content 5 | {% endblock %} -------------------------------------------------------------------------------- /sites/s3/pages/blank.twig: -------------------------------------------------------------------------------- 1 | {% layout %} 2 | 3 | {% block workarea %} 4 | 5 | {{ component([controllerClass, controllerAction]) }} 6 | 7 | {% endblock %} -------------------------------------------------------------------------------- /sites/s3/pages/index.twig: -------------------------------------------------------------------------------- 1 | {% layout %} 2 | 3 | {% block workarea %} 4 | 5 | {{ component( 6 | ['Bitrix\\Main\\Components\\DemoForumSection\\Controller', 'main'], 7 | { 8 | 9 | }, 10 | 'clientForumComponent') 11 | }} 12 | 13 | {% endblock %} --------------------------------------------------------------------------------