diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 394faa9ceb..d8d15ced42 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -553,36 +553,38 @@ jobs: # A tag keeps its builds for 30 days, other runs for 5 retention-days: ${{ startsWith(github.ref, 'refs/tags/') && 30 || 5 }} - build_extension_macos: - permissions: - contents: read - - name: Build / PHP-${{ matrix.php }}-${{ matrix.ts }}-${{ matrix.name }}-${{ matrix.arch }} - needs: [generate_source] - runs-on: ${{ matrix.os }} - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - php: - - '8.1' - - '8.2' - - '8.3' - - '8.4' - - '8.5' - ts: - - 'nts' - - 'ts' - arch: - - 'arm64' - - name: - - macos-clang - - include: - - { name: macos-clang, arch: arm64, os: macos-14, compiler: clang } - - steps: *build_extension_steps + # The macOS builds fail on most runs. They are disabled until the + # macOS runners are stable again. + # build_extension_macos: + # permissions: + # contents: read + + # name: Build / PHP-${{ matrix.php }}-${{ matrix.ts }}-${{ matrix.name }}-${{ matrix.arch }} + # needs: [generate_source] + # runs-on: ${{ matrix.os }} + # timeout-minutes: 30 + # strategy: + # fail-fast: false + # matrix: + # php: + # - '8.1' + # - '8.2' + # - '8.3' + # - '8.4' + # - '8.5' + # ts: + # - 'nts' + # - 'ts' + # arch: + # - 'arm64' + + # name: + # - macos-clang + + # include: + # - { name: macos-clang, arch: arm64, os: macos-14, compiler: clang } + + # steps: *build_extension_steps unit_test: permissions: @@ -1223,7 +1225,7 @@ jobs: - generate_source - generate_pecl - build_extension - - build_extension_macos + # - build_extension_macos - build_extension_windows - unit_test - database_mariadb_test diff --git a/CHANGELOG-5.0.md b/CHANGELOG-5.0.md index 8342b21acf..a108bae211 100644 --- a/CHANGELOG-5.0.md +++ b/CHANGELOG-5.0.md @@ -2,6 +2,24 @@ All notable changes are documented here. The format is based on [Keep a Changelog][keep_a_changelog] and this project adheres to [Semantic Versioning][semantic_versioning]. +## [Unreleased](https://github.com/phalcon/cphalcon/releases/tag/vx.x.x) (2026-xx-xx) + +### Tools + +- Zephir 1.5.0 + +### Changed + +### Added + +### Fixed + +- `Phalcon\Mvc\View::partial()` and `Phalcon\Mvc\View\Simple::render()` dropping a `..` path segment instead of resolving it. [#17606](https://github.com/phalcon/cphalcon/issues/17606) [[doc]](https://docs.phalcon.io/5.22/views/) +- `Phalcon\Mvc\View::partial()` not detecting a Windows absolute path (`C:\...`) as absolute. [#17606](https://github.com/phalcon/cphalcon/issues/17606) [[doc]](https://docs.phalcon.io/5.22/views/) + +### Removed + + ## [5.22.0](https://github.com/phalcon/cphalcon/releases/tag/v5.22.0) (2026-09-22) ### Tools diff --git a/README.md b/README.md index dd3ba52b12..d4c2032879 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,8 @@ Support us with a monthly donation and help us continue our activities. [[Become OpenCollective Backers +![Alt](https://repobeats.axiom.co/api/embed/8ab44186e80c2f075c6b51603fd7cf2a80a2cb33.svg "Repobeats analytics image") + ## License Phalcon is open-source software licensed under the BSD 3-Clause License. diff --git a/ext/phalcon/mvc/view.zep.c b/ext/phalcon/mvc/view.zep.c index 760f80e508..fafa847d45 100644 --- a/ext/phalcon/mvc/view.zep.c +++ b/ext/phalcon/mvc/view.zep.c @@ -1098,10 +1098,11 @@ PHP_METHOD(Phalcon_Mvc_View, isDisabled) */ PHP_METHOD(Phalcon_Mvc_View, partial) { - zend_bool _10$$4; + zend_bool _10$$6; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zephir_fcall_cache_entry *_12 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; - zval *partialPath_param = NULL, *params = NULL, params_sub, __$null, segment, segments, viewParams, _3, _4, _5, _6, *_7, _8, *_9, _11, _12, _13, _14, _15, _0$$3, _1$$3, _2$$3; + zval *partialPath_param = NULL, *params = NULL, params_sub, __$null, segment, segments, viewParams, _3, _4, _5, _6, *_7, _8, *_9, _13, _14, _15, _16, _17, _0$$3, _1$$3, _2$$3, _11$$6; zval partialPath; zval *this_ptr = getThis(); @@ -1116,14 +1117,15 @@ PHP_METHOD(Phalcon_Mvc_View, partial) ZVAL_UNDEF(&_5); ZVAL_UNDEF(&_6); ZVAL_UNDEF(&_8); - ZVAL_UNDEF(&_11); - ZVAL_UNDEF(&_12); ZVAL_UNDEF(&_13); ZVAL_UNDEF(&_14); ZVAL_UNDEF(&_15); + ZVAL_UNDEF(&_16); + ZVAL_UNDEF(&_17); ZVAL_UNDEF(&_0$$3); ZVAL_UNDEF(&_1$$3); ZVAL_UNDEF(&_2$$3); + ZVAL_UNDEF(&_11$$6); static zend_string *_zephir_prop_0 = NULL; static zend_string *_zephir_prop_1 = NULL; if (UNEXPECTED(!_zephir_prop_0)) { @@ -1176,30 +1178,42 @@ PHP_METHOD(Phalcon_Mvc_View, partial) } else { _7 = &_3; } - zephir_is_iterable(_7, 0, "phalcon/Mvc/View.zep", 619); + zephir_is_iterable(_7, 0, "phalcon/Mvc/View.zep", 631); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(_7), _9) { ZEPHIR_INIT_NVAR(&segment); ZVAL_COPY(&segment, _9); - _10$$4 = !ZEPHIR_IS_STRING_IDENTICAL(&segment, "."); - if (_10$$4) { - _10$$4 = !ZEPHIR_IS_STRING_IDENTICAL(&segment, ".."); + if (ZEPHIR_IS_STRING_IDENTICAL(&segment, ".")) { + continue; } - if (_10$$4) { - zephir_array_append(&segments, &segment, PH_SEPARATE, "phalcon/Mvc/View.zep", 615); + if (ZEPHIR_IS_STRING_IDENTICAL(&segment, "..")) { + _10$$6 = zephir_fast_count_int(&segments) > 0; + if (_10$$6) { + ZEPHIR_OBS_NVAR(&_11$$6); + zephir_array_fetch_long(&_11$$6, &segments, (zephir_fast_count_int(&segments) - 1), PH_NOISY, "phalcon/Mvc/View.zep", 621); + _10$$6 = !ZEPHIR_IS_STRING_IDENTICAL(&_11$$6, ""); + } + if (_10$$6) { + ZEPHIR_MAKE_REF(&segments); + ZEPHIR_CALL_FUNCTION(NULL, "array_pop", &_12, 353, &segments); + ZEPHIR_UNREF(&segments); + zephir_check_call_status(); + } + continue; } + zephir_array_append(&segments, &segment, PH_SEPARATE, "phalcon/Mvc/View.zep", 628); } ZEND_HASH_FOREACH_END(); ZEPHIR_INIT_NVAR(&segment); ZEPHIR_INIT_NVAR(&partialPath); zephir_fast_join_str(&partialPath, SL("/"), &segments); - ZEPHIR_CALL_METHOD(&_11, this_ptr, "loadtemplateengines", NULL, 0); + ZEPHIR_CALL_METHOD(&_13, this_ptr, "loadtemplateengines", NULL, 0); zephir_check_call_status(); - zephir_read_property_cached(&_12, this_ptr, _zephir_prop_1, 1190, PH_NOISY_CC | PH_READONLY); - ZEPHIR_INIT_VAR(&_13); - ZEPHIR_CONCAT_VV(&_13, &_12, &partialPath); - ZVAL_BOOL(&_14, 0); - ZVAL_BOOL(&_15, 0); - ZEPHIR_CALL_METHOD(NULL, this_ptr, "enginerender", NULL, 0, &_11, &_13, &_14, &_15); + zephir_read_property_cached(&_14, this_ptr, _zephir_prop_1, 1190, PH_NOISY_CC | PH_READONLY); + ZEPHIR_INIT_VAR(&_15); + ZEPHIR_CONCAT_VV(&_15, &_14, &partialPath); + ZVAL_BOOL(&_16, 0); + ZVAL_BOOL(&_17, 0); + ZEPHIR_CALL_METHOD(NULL, this_ptr, "enginerender", NULL, 0, &_13, &_15, &_16, &_17); zephir_check_call_status(); if (Z_TYPE_P(params) == IS_ARRAY) { zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 1183, &viewParams); @@ -1252,18 +1266,18 @@ PHP_METHOD(Phalcon_Mvc_View, pick) } else { ZEPHIR_INIT_VAR(&layout); ZVAL_NULL(&layout); - if (zephir_memnstr_str(renderView, SL("/"), "phalcon/Mvc/View.zep", 666)) { + if (zephir_memnstr_str(renderView, SL("/"), "phalcon/Mvc/View.zep", 678)) { ZEPHIR_INIT_VAR(&parts); zephir_fast_explode_str(&parts, SL("/"), renderView, ZEND_LONG_MAX); ZEPHIR_OBS_NVAR(&layout); - zephir_array_fetch_long(&layout, &parts, 0, PH_NOISY, "phalcon/Mvc/View.zep", 668); + zephir_array_fetch_long(&layout, &parts, 0, PH_NOISY, "phalcon/Mvc/View.zep", 680); } ZEPHIR_INIT_VAR(&_0$$4); zephir_create_array(&_0$$4, 1, 0); zephir_array_fast_append(&_0$$4, renderView); ZEPHIR_CPY_WRT(&pickView, &_0$$4); if (Z_TYPE_P(&layout) != IS_NULL) { - zephir_array_append(&pickView, &layout, PH_SEPARATE, "phalcon/Mvc/View.zep", 674); + zephir_array_append(&pickView, &layout, PH_SEPARATE, "phalcon/Mvc/View.zep", 686); } } zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 1191, &pickView); @@ -1455,7 +1469,7 @@ PHP_METHOD(Phalcon_Mvc_View, processRender) ZEPHIR_CPY_WRT(&renderView, &_3$$7); } else { ZEPHIR_OBS_NVAR(&renderView); - zephir_array_fetch_long(&renderView, &pickView, 0, PH_NOISY, "phalcon/Mvc/View.zep", 755); + zephir_array_fetch_long(&renderView, &pickView, 0, PH_NOISY, "phalcon/Mvc/View.zep", 767); if (Z_TYPE_P(&layoutName) == IS_NULL) { zephir_memory_observe(&pickViewAction); if (zephir_array_isset_long_fetch(&pickViewAction, &pickView, 1, 0)) { @@ -1520,7 +1534,7 @@ PHP_METHOD(Phalcon_Mvc_View, processRender) } else { _12$$17 = &templatesBefore; } - zephir_is_iterable(_12$$17, 0, "phalcon/Mvc/View.zep", 831); + zephir_is_iterable(_12$$17, 0, "phalcon/Mvc/View.zep", 843); if (Z_TYPE_P(_12$$17) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(_12$$17), _14$$17) { @@ -1600,7 +1614,7 @@ PHP_METHOD(Phalcon_Mvc_View, processRender) } else { _24$$23 = &templatesAfter; } - zephir_is_iterable(_24$$23, 0, "phalcon/Mvc/View.zep", 867); + zephir_is_iterable(_24$$23, 0, "phalcon/Mvc/View.zep", 879); if (Z_TYPE_P(_24$$23) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(_24$$23), _26$$23) { @@ -2230,7 +2244,7 @@ PHP_METHOD(Phalcon_Mvc_View, setViewsDir) object_init_ex(&_1$$3, phalcon_mvc_view_exceptions_invalidviewsdirtype_ce); ZEPHIR_CALL_METHOD(NULL, &_1$$3, "__construct", NULL, 0); zephir_check_call_status(); - zephir_throw_exception_debug(&_1$$3, "phalcon/Mvc/View.zep", 1143); + zephir_throw_exception_debug(&_1$$3, "phalcon/Mvc/View.zep", 1155); ZEPHIR_MM_RESTORE(); return; } @@ -2248,7 +2262,7 @@ PHP_METHOD(Phalcon_Mvc_View, setViewsDir) } else { _3$$5 = viewsDir; } - zephir_is_iterable(_3$$5, 0, "phalcon/Mvc/View.zep", 1159); + zephir_is_iterable(_3$$5, 0, "phalcon/Mvc/View.zep", 1171); if (Z_TYPE_P(_3$$5) == IS_ARRAY) { ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(_3$$5), _6$$5, _7$$5, _5$$5) { @@ -2265,7 +2279,7 @@ PHP_METHOD(Phalcon_Mvc_View, setViewsDir) object_init_ex(&_8$$7, phalcon_mvc_view_exceptions_viewsdiritemmustbestring_ce); ZEPHIR_CALL_METHOD(NULL, &_8$$7, "__construct", &_9, 0); zephir_check_call_status(); - zephir_throw_exception_debug(&_8$$7, "phalcon/Mvc/View.zep", 1153); + zephir_throw_exception_debug(&_8$$7, "phalcon/Mvc/View.zep", 1165); ZEPHIR_MM_RESTORE(); return; } @@ -2298,7 +2312,7 @@ PHP_METHOD(Phalcon_Mvc_View, setViewsDir) object_init_ex(&_13$$9, phalcon_mvc_view_exceptions_viewsdiritemmustbestring_ce); ZEPHIR_CALL_METHOD(NULL, &_13$$9, "__construct", &_9, 0); zephir_check_call_status(); - zephir_throw_exception_debug(&_13$$9, "phalcon/Mvc/View.zep", 1153); + zephir_throw_exception_debug(&_13$$9, "phalcon/Mvc/View.zep", 1165); ZEPHIR_MM_RESTORE(); return; } @@ -2515,7 +2529,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) } else { _2 = &_1; } - zephir_is_iterable(_2, 0, "phalcon/Mvc/View.zep", 1270); + zephir_is_iterable(_2, 0, "phalcon/Mvc/View.zep", 1282); if (Z_TYPE_P(_2) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(_2), _4) { @@ -2529,7 +2543,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) } else { ZEPHIR_CPY_WRT(&viewsDirPath, &viewPath_zv); } - zephir_is_iterable(&engines, 0, "phalcon/Mvc/View.zep", 1265); + zephir_is_iterable(&engines, 0, "phalcon/Mvc/View.zep", 1277); if (Z_TYPE_P(&engines) == IS_ARRAY) { ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(&engines), _8$$3, _9$$3, _7$$3) { @@ -2574,7 +2588,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) } RETURN_MM_NULL(); } - zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1263); + zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1275); } ZEND_HASH_FOREACH_END(); } else { ZEPHIR_CALL_METHOD(NULL, &engines, "rewind", NULL, 0); @@ -2629,7 +2643,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) } RETURN_MM_NULL(); } - zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1263); + zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1275); } } ZEPHIR_INIT_NVAR(&engine); @@ -2661,7 +2675,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) } else { ZEPHIR_CPY_WRT(&viewsDirPath, &viewPath_zv); } - zephir_is_iterable(&engines, 0, "phalcon/Mvc/View.zep", 1265); + zephir_is_iterable(&engines, 0, "phalcon/Mvc/View.zep", 1277); if (Z_TYPE_P(&engines) == IS_ARRAY) { ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(&engines), _33$$16, _34$$16, _32$$16) { @@ -2706,7 +2720,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) } RETURN_MM_NULL(); } - zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1263); + zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1275); } ZEND_HASH_FOREACH_END(); } else { ZEPHIR_CALL_METHOD(NULL, &engines, "rewind", NULL, 0); @@ -2761,7 +2775,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) } RETURN_MM_NULL(); } - zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1263); + zephir_array_append(&viewEnginePaths, &viewEnginePath, PH_SEPARATE, "phalcon/Mvc/View.zep", 1275); } } ZEPHIR_INIT_NVAR(&engine); @@ -2781,7 +2795,7 @@ PHP_METHOD(Phalcon_Mvc_View, engineRender) object_init_ex(&_54$$30, phalcon_mvc_view_exceptions_viewnotfound_ce); ZEPHIR_CALL_METHOD(NULL, &_54$$30, "__construct", NULL, 0, &viewPath_zv); zephir_check_call_status(); - zephir_throw_exception_debug(&_54$$30, "phalcon/Mvc/View.zep", 1277); + zephir_throw_exception_debug(&_54$$30, "phalcon/Mvc/View.zep", 1289); ZEPHIR_MM_RESTORE(); return; } @@ -2825,18 +2839,21 @@ PHP_METHOD(Phalcon_Mvc_View, getViewsDirs) */ PHP_METHOD(Phalcon_Mvc_View, isAbsolutePath) { - zend_bool _1$$3; + zend_bool _1$$3, _5$$3; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; - zval path_zv, _0, _5, _2$$3, _3$$3, _4$$3; + zval path_zv, _0, _9, _2$$3, _3$$3, _4$$3, _6$$3, _7$$3, _8$$3; zend_string *path = NULL; ZVAL_UNDEF(&path_zv); ZVAL_UNDEF(&_0); - ZVAL_UNDEF(&_5); + ZVAL_UNDEF(&_9); ZVAL_UNDEF(&_2$$3); ZVAL_UNDEF(&_3$$3); ZVAL_UNDEF(&_4$$3); + ZVAL_UNDEF(&_6$$3); + ZVAL_UNDEF(&_7$$3); + ZVAL_UNDEF(&_8$$3); ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STR(path) ZEND_PARSE_PARAMETERS_END(); @@ -2853,13 +2870,21 @@ PHP_METHOD(Phalcon_Mvc_View, isAbsolutePath) ZVAL_LONG(&_3$$3, 2); ZEPHIR_INIT_VAR(&_4$$3); zephir_substr(&_4$$3, &path_zv, 1 , 2 , 0); - _1$$3 = ZEPHIR_IS_STRING_IDENTICAL(&_4$$3, ":\\"); + _5$$3 = ZEPHIR_IS_STRING_IDENTICAL(&_4$$3, ":\\"); + if (!(_5$$3)) { + ZVAL_LONG(&_6$$3, 1); + ZVAL_LONG(&_7$$3, 2); + ZEPHIR_INIT_VAR(&_8$$3); + zephir_substr(&_8$$3, &path_zv, 1 , 2 , 0); + _5$$3 = ZEPHIR_IS_STRING_IDENTICAL(&_8$$3, ":/"); + } + _1$$3 = _5$$3; } RETURN_MM_BOOL(_1$$3); } - ZEPHIR_INIT_VAR(&_5); - ZVAL_STRING(&_5, "/"); - ZEPHIR_RETURN_CALL_FUNCTION("str_starts_with", NULL, 0, &path_zv, &_5); + ZEPHIR_INIT_VAR(&_9); + ZVAL_STRING(&_9, "/"); + ZEPHIR_RETURN_CALL_FUNCTION("str_starts_with", NULL, 0, &path_zv, &_9); zephir_check_call_status(); RETURN_MM(); } @@ -2938,7 +2963,7 @@ PHP_METHOD(Phalcon_Mvc_View, loadTemplateEngines) object_init_ex(&_3$$6, phalcon_mvc_view_exceptions_viewservicesunavailable_ce); ZEPHIR_CALL_METHOD(NULL, &_3$$6, "__construct", NULL, 0); zephir_check_call_status(); - zephir_throw_exception_debug(&_3$$6, "phalcon/Mvc/View.zep", 1335); + zephir_throw_exception_debug(&_3$$6, "phalcon/Mvc/View.zep", 1348); ZEPHIR_MM_RESTORE(); return; } @@ -2949,7 +2974,7 @@ PHP_METHOD(Phalcon_Mvc_View, loadTemplateEngines) } else { _4$$5 = ®isteredEngines; } - zephir_is_iterable(_4$$5, 0, "phalcon/Mvc/View.zep", 1370); + zephir_is_iterable(_4$$5, 0, "phalcon/Mvc/View.zep", 1383); if (Z_TYPE_P(_4$$5) == IS_ARRAY) { ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(_4$$5), _7$$5, _8$$5, _6$$5) { @@ -2979,7 +3004,7 @@ PHP_METHOD(Phalcon_Mvc_View, loadTemplateEngines) object_init_ex(&_13$$12, phalcon_mvc_view_exceptions_invalidengineregistration_ce); ZEPHIR_CALL_METHOD(NULL, &_13$$12, "__construct", &_14, 0, &extension); zephir_check_call_status(); - zephir_throw_exception_debug(&_13$$12, "phalcon/Mvc/View.zep", 1361); + zephir_throw_exception_debug(&_13$$12, "phalcon/Mvc/View.zep", 1374); ZEPHIR_MM_RESTORE(); return; } @@ -3029,7 +3054,7 @@ PHP_METHOD(Phalcon_Mvc_View, loadTemplateEngines) object_init_ex(&_21$$18, phalcon_mvc_view_exceptions_invalidengineregistration_ce); ZEPHIR_CALL_METHOD(NULL, &_21$$18, "__construct", &_14, 0, &extension); zephir_check_call_status(); - zephir_throw_exception_debug(&_21$$18, "phalcon/Mvc/View.zep", 1361); + zephir_throw_exception_debug(&_21$$18, "phalcon/Mvc/View.zep", 1374); ZEPHIR_MM_RESTORE(); return; } diff --git a/ext/phalcon/mvc/view/simple.zep.c b/ext/phalcon/mvc/view/simple.zep.c index cd232738fa..8c845a334c 100644 --- a/ext/phalcon/mvc/view/simple.zep.c +++ b/ext/phalcon/mvc/view/simple.zep.c @@ -799,13 +799,13 @@ PHP_METHOD(Phalcon_Mvc_View_Simple, loadTemplateEngines) */ PHP_METHOD(Phalcon_Mvc_View_Simple, internalRender) { - zend_string *_16; - zend_ulong _15; - zend_bool notExists = 0, mustClean = 0, _32, _10$$6, _22$$8, _37$$15; + zend_string *_18; + zend_ulong _17; + zend_bool notExists = 0, mustClean = 0, _34, _10$$8, _24$$10, _39$$17; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; - zephir_fcall_cache_entry *_19 = NULL, *_27 = NULL, *_30 = NULL, *_42 = NULL, *_45 = NULL; + zephir_fcall_cache_entry *_12 = NULL, *_21 = NULL, *_29 = NULL, *_32 = NULL, *_44 = NULL, *_47 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; - zval *path_param = NULL, *params, params_sub, eventsManager, engines, extension, engine, segment, segments, _0, _3, _4, _5, _6, *_7, _8, *_9, _11, *_12, _13, *_14, _31, _1$$4, _2$$4, _17$$8, _18$$8, _20$$8, _21$$8, _23$$8, _28$$8, _24$$9, _25$$12, _26$$12, _29$$14, _33$$15, _34$$15, _35$$15, _36$$15, _38$$15, _43$$15, _39$$16, _40$$19, _41$$19, _44$$21, _46$$22, _47$$23; + zval *path_param = NULL, *params, params_sub, eventsManager, engines, extension, engine, segment, segments, _0, _3, _4, _5, _6, *_7, _8, *_9, _13, *_14, _15, *_16, _33, _1$$4, _2$$4, _11$$8, _19$$10, _20$$10, _22$$10, _23$$10, _25$$10, _30$$10, _26$$11, _27$$14, _28$$14, _31$$16, _35$$17, _36$$17, _37$$17, _38$$17, _40$$17, _45$$17, _41$$18, _42$$21, _43$$21, _46$$23, _48$$24, _49$$25; zval path, viewEnginePath, viewsDirPath; zval *this_ptr = getThis(); @@ -825,33 +825,34 @@ PHP_METHOD(Phalcon_Mvc_View_Simple, internalRender) ZVAL_UNDEF(&_5); ZVAL_UNDEF(&_6); ZVAL_UNDEF(&_8); - ZVAL_UNDEF(&_11); ZVAL_UNDEF(&_13); - ZVAL_UNDEF(&_31); + ZVAL_UNDEF(&_15); + ZVAL_UNDEF(&_33); ZVAL_UNDEF(&_1$$4); ZVAL_UNDEF(&_2$$4); - ZVAL_UNDEF(&_17$$8); - ZVAL_UNDEF(&_18$$8); - ZVAL_UNDEF(&_20$$8); - ZVAL_UNDEF(&_21$$8); - ZVAL_UNDEF(&_23$$8); - ZVAL_UNDEF(&_28$$8); - ZVAL_UNDEF(&_24$$9); - ZVAL_UNDEF(&_25$$12); - ZVAL_UNDEF(&_26$$12); - ZVAL_UNDEF(&_29$$14); - ZVAL_UNDEF(&_33$$15); - ZVAL_UNDEF(&_34$$15); - ZVAL_UNDEF(&_35$$15); - ZVAL_UNDEF(&_36$$15); - ZVAL_UNDEF(&_38$$15); - ZVAL_UNDEF(&_43$$15); - ZVAL_UNDEF(&_39$$16); - ZVAL_UNDEF(&_40$$19); - ZVAL_UNDEF(&_41$$19); - ZVAL_UNDEF(&_44$$21); - ZVAL_UNDEF(&_46$$22); - ZVAL_UNDEF(&_47$$23); + ZVAL_UNDEF(&_11$$8); + ZVAL_UNDEF(&_19$$10); + ZVAL_UNDEF(&_20$$10); + ZVAL_UNDEF(&_22$$10); + ZVAL_UNDEF(&_23$$10); + ZVAL_UNDEF(&_25$$10); + ZVAL_UNDEF(&_30$$10); + ZVAL_UNDEF(&_26$$11); + ZVAL_UNDEF(&_27$$14); + ZVAL_UNDEF(&_28$$14); + ZVAL_UNDEF(&_31$$16); + ZVAL_UNDEF(&_35$$17); + ZVAL_UNDEF(&_36$$17); + ZVAL_UNDEF(&_37$$17); + ZVAL_UNDEF(&_38$$17); + ZVAL_UNDEF(&_40$$17); + ZVAL_UNDEF(&_45$$17); + ZVAL_UNDEF(&_41$$18); + ZVAL_UNDEF(&_42$$21); + ZVAL_UNDEF(&_43$$21); + ZVAL_UNDEF(&_46$$23); + ZVAL_UNDEF(&_48$$24); + ZVAL_UNDEF(&_49$$25); static zend_string *_zephir_prop_0 = NULL; static zend_string *_zephir_prop_1 = NULL; static zend_string *_zephir_prop_2 = NULL; @@ -908,161 +909,173 @@ PHP_METHOD(Phalcon_Mvc_View_Simple, internalRender) } else { _7 = &_3; } - zephir_is_iterable(_7, 0, "phalcon/Mvc/View/Simple.zep", 473); + zephir_is_iterable(_7, 0, "phalcon/Mvc/View/Simple.zep", 485); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(_7), _9) { ZEPHIR_INIT_NVAR(&segment); ZVAL_COPY(&segment, _9); - _10$$6 = !ZEPHIR_IS_STRING_IDENTICAL(&segment, "."); - if (_10$$6) { - _10$$6 = !ZEPHIR_IS_STRING_IDENTICAL(&segment, ".."); + if (ZEPHIR_IS_STRING_IDENTICAL(&segment, ".")) { + continue; } - if (_10$$6) { - zephir_array_append(&segments, &segment, PH_SEPARATE, "phalcon/Mvc/View/Simple.zep", 469); + if (ZEPHIR_IS_STRING_IDENTICAL(&segment, "..")) { + _10$$8 = zephir_fast_count_int(&segments) > 0; + if (_10$$8) { + ZEPHIR_OBS_NVAR(&_11$$8); + zephir_array_fetch_long(&_11$$8, &segments, (zephir_fast_count_int(&segments) - 1), PH_NOISY, "phalcon/Mvc/View/Simple.zep", 475); + _10$$8 = !ZEPHIR_IS_STRING_IDENTICAL(&_11$$8, ""); + } + if (_10$$8) { + ZEPHIR_MAKE_REF(&segments); + ZEPHIR_CALL_FUNCTION(NULL, "array_pop", &_12, 353, &segments); + ZEPHIR_UNREF(&segments); + zephir_check_call_status(); + } + continue; } + zephir_array_append(&segments, &segment, PH_SEPARATE, "phalcon/Mvc/View/Simple.zep", 482); } ZEND_HASH_FOREACH_END(); ZEPHIR_INIT_NVAR(&segment); ZEPHIR_INIT_NVAR(&path); zephir_fast_join_str(&path, SL("/"), &segments); zephir_read_property_cached(&_0, this_ptr, _zephir_prop_2, 1237, PH_NOISY_CC | PH_READONLY); - ZEPHIR_INIT_VAR(&_11); - ZEPHIR_CONCAT_VV(&_11, &_0, &path); - zephir_get_strval(&viewsDirPath, &_11); + ZEPHIR_INIT_VAR(&_13); + ZEPHIR_CONCAT_VV(&_13, &_0, &path); + zephir_get_strval(&viewsDirPath, &_13); ZEPHIR_CALL_METHOD(&engines, this_ptr, "loadtemplateengines", NULL, 0); zephir_check_call_status(); if (Z_TYPE_P(&engines) == IS_STRING) { - ZEPHIR_INIT_VAR(&_13); - zephir_string_to_char_array(&_13, &engines); - _12 = &_13; + ZEPHIR_INIT_VAR(&_15); + zephir_string_to_char_array(&_15, &engines); + _14 = &_15; } else { - _12 = &engines; + _14 = &engines; } - zephir_is_iterable(_12, 0, "phalcon/Mvc/View/Simple.zep", 524); - if (Z_TYPE_P(_12) == IS_ARRAY) { - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(_12), _15, _16, _14) + zephir_is_iterable(_14, 0, "phalcon/Mvc/View/Simple.zep", 536); + if (Z_TYPE_P(_14) == IS_ARRAY) { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(_14), _17, _18, _16) { ZEPHIR_INIT_NVAR(&extension); - if (_16 != NULL) { - ZVAL_STR_COPY(&extension, _16); + if (_18 != NULL) { + ZVAL_STR_COPY(&extension, _18); } else { - ZVAL_LONG(&extension, _15); + ZVAL_LONG(&extension, _17); } ZEPHIR_INIT_NVAR(&engine); - ZVAL_COPY(&engine, _14); - ZEPHIR_INIT_NVAR(&_18$$8); - ZEPHIR_CONCAT_VV(&_18$$8, &viewsDirPath, &extension); - ZEPHIR_CALL_METHOD(&_17$$8, this_ptr, "phpfileexists", &_19, 0, &_18$$8); + ZVAL_COPY(&engine, _16); + ZEPHIR_INIT_NVAR(&_20$$10); + ZEPHIR_CONCAT_VV(&_20$$10, &viewsDirPath, &extension); + ZEPHIR_CALL_METHOD(&_19$$10, this_ptr, "phpfileexists", &_21, 0, &_20$$10); zephir_check_call_status(); - if (zephir_is_true(&_17$$8)) { - ZEPHIR_INIT_NVAR(&_24$$9); - ZEPHIR_CONCAT_VV(&_24$$9, &viewsDirPath, &extension); - zephir_get_strval(&viewEnginePath, &_24$$9); + if (zephir_is_true(&_19$$10)) { + ZEPHIR_INIT_NVAR(&_26$$11); + ZEPHIR_CONCAT_VV(&_26$$11, &viewsDirPath, &extension); + zephir_get_strval(&viewEnginePath, &_26$$11); } else { - ZVAL_LONG(&_20$$8, -zephir_fast_strlen_ev(&extension)); - ZEPHIR_INIT_NVAR(&_21$$8); - zephir_substr(&_21$$8, &viewsDirPath, zephir_get_intval(&_20$$8), 0, ZEPHIR_SUBSTR_NO_LENGTH); - _22$$8 = ZEPHIR_IS_EQUAL(&_21$$8, &extension); - if (_22$$8) { - ZEPHIR_CALL_METHOD(&_23$$8, this_ptr, "phpfileexists", &_19, 0, &viewsDirPath); + ZVAL_LONG(&_22$$10, -zephir_fast_strlen_ev(&extension)); + ZEPHIR_INIT_NVAR(&_23$$10); + zephir_substr(&_23$$10, &viewsDirPath, zephir_get_intval(&_22$$10), 0, ZEPHIR_SUBSTR_NO_LENGTH); + _24$$10 = ZEPHIR_IS_EQUAL(&_23$$10, &extension); + if (_24$$10) { + ZEPHIR_CALL_METHOD(&_25$$10, this_ptr, "phpfileexists", &_21, 0, &viewsDirPath); zephir_check_call_status(); - _22$$8 = zephir_is_true(&_23$$8); + _24$$10 = zephir_is_true(&_25$$10); } - if (_22$$8) { + if (_24$$10) { ZEPHIR_CPY_WRT(&viewEnginePath, &viewsDirPath); } else { continue; } } if (Z_TYPE_P(&eventsManager) == IS_OBJECT) { - ZEPHIR_INIT_NVAR(&_26$$12); - ZVAL_STRING(&_26$$12, "view:beforeRenderView"); - ZEPHIR_CALL_METHOD(&_25$$12, &eventsManager, "fire", &_27, 0, &_26$$12, this_ptr, &viewEnginePath); + ZEPHIR_INIT_NVAR(&_28$$14); + ZVAL_STRING(&_28$$14, "view:beforeRenderView"); + ZEPHIR_CALL_METHOD(&_27$$14, &eventsManager, "fire", &_29, 0, &_28$$14, this_ptr, &viewEnginePath); zephir_check_call_status(); - if (ZEPHIR_IS_FALSE_IDENTICAL(&_25$$12)) { + if (ZEPHIR_IS_FALSE_IDENTICAL(&_27$$14)) { continue; } } if (mustClean) { - ZVAL_BOOL(&_28$$8, 1); + ZVAL_BOOL(&_30$$10, 1); } else { - ZVAL_BOOL(&_28$$8, 0); + ZVAL_BOOL(&_30$$10, 0); } - ZEPHIR_CALL_METHOD(NULL, &engine, "render", NULL, 0, &viewEnginePath, params, &_28$$8); + ZEPHIR_CALL_METHOD(NULL, &engine, "render", NULL, 0, &viewEnginePath, params, &_30$$10); zephir_check_call_status(); notExists = 0; if (Z_TYPE_P(&eventsManager) == IS_OBJECT) { - ZEPHIR_INIT_NVAR(&_29$$14); - ZVAL_STRING(&_29$$14, "view:afterRenderView"); - ZEPHIR_CALL_METHOD(NULL, &eventsManager, "fire", &_30, 0, &_29$$14, this_ptr); + ZEPHIR_INIT_NVAR(&_31$$16); + ZVAL_STRING(&_31$$16, "view:afterRenderView"); + ZEPHIR_CALL_METHOD(NULL, &eventsManager, "fire", &_32, 0, &_31$$16, this_ptr); zephir_check_call_status(); } break; } ZEND_HASH_FOREACH_END(); } else { - ZEPHIR_CALL_METHOD(NULL, _12, "rewind", NULL, 0); + ZEPHIR_CALL_METHOD(NULL, _14, "rewind", NULL, 0); zephir_check_call_status(); - _32 = 1; + _34 = 1; while (1) { - if (_32) { - _32 = 0; + if (_34) { + _34 = 0; } else { - ZEPHIR_CALL_METHOD(NULL, _12, "next", NULL, 0); + ZEPHIR_CALL_METHOD(NULL, _14, "next", NULL, 0); zephir_check_call_status(); } - ZEPHIR_CALL_METHOD(&_31, _12, "valid", NULL, 0); + ZEPHIR_CALL_METHOD(&_33, _14, "valid", NULL, 0); zephir_check_call_status(); - if (!zend_is_true(&_31)) { + if (!zend_is_true(&_33)) { break; } - ZEPHIR_CALL_METHOD(&extension, _12, "key", NULL, 0); + ZEPHIR_CALL_METHOD(&extension, _14, "key", NULL, 0); zephir_check_call_status(); - ZEPHIR_CALL_METHOD(&engine, _12, "current", NULL, 0); + ZEPHIR_CALL_METHOD(&engine, _14, "current", NULL, 0); zephir_check_call_status(); - ZEPHIR_INIT_NVAR(&_34$$15); - ZEPHIR_CONCAT_VV(&_34$$15, &viewsDirPath, &extension); - ZEPHIR_CALL_METHOD(&_33$$15, this_ptr, "phpfileexists", &_19, 0, &_34$$15); + ZEPHIR_INIT_NVAR(&_36$$17); + ZEPHIR_CONCAT_VV(&_36$$17, &viewsDirPath, &extension); + ZEPHIR_CALL_METHOD(&_35$$17, this_ptr, "phpfileexists", &_21, 0, &_36$$17); zephir_check_call_status(); - if (zephir_is_true(&_33$$15)) { - ZEPHIR_INIT_NVAR(&_39$$16); - ZEPHIR_CONCAT_VV(&_39$$16, &viewsDirPath, &extension); - zephir_get_strval(&viewEnginePath, &_39$$16); + if (zephir_is_true(&_35$$17)) { + ZEPHIR_INIT_NVAR(&_41$$18); + ZEPHIR_CONCAT_VV(&_41$$18, &viewsDirPath, &extension); + zephir_get_strval(&viewEnginePath, &_41$$18); } else { - ZVAL_LONG(&_35$$15, -zephir_fast_strlen_ev(&extension)); - ZEPHIR_INIT_NVAR(&_36$$15); - zephir_substr(&_36$$15, &viewsDirPath, zephir_get_intval(&_35$$15), 0, ZEPHIR_SUBSTR_NO_LENGTH); - _37$$15 = ZEPHIR_IS_EQUAL(&_36$$15, &extension); - if (_37$$15) { - ZEPHIR_CALL_METHOD(&_38$$15, this_ptr, "phpfileexists", &_19, 0, &viewsDirPath); + ZVAL_LONG(&_37$$17, -zephir_fast_strlen_ev(&extension)); + ZEPHIR_INIT_NVAR(&_38$$17); + zephir_substr(&_38$$17, &viewsDirPath, zephir_get_intval(&_37$$17), 0, ZEPHIR_SUBSTR_NO_LENGTH); + _39$$17 = ZEPHIR_IS_EQUAL(&_38$$17, &extension); + if (_39$$17) { + ZEPHIR_CALL_METHOD(&_40$$17, this_ptr, "phpfileexists", &_21, 0, &viewsDirPath); zephir_check_call_status(); - _37$$15 = zephir_is_true(&_38$$15); + _39$$17 = zephir_is_true(&_40$$17); } - if (_37$$15) { + if (_39$$17) { ZEPHIR_CPY_WRT(&viewEnginePath, &viewsDirPath); } else { continue; } } if (Z_TYPE_P(&eventsManager) == IS_OBJECT) { - ZEPHIR_INIT_NVAR(&_41$$19); - ZVAL_STRING(&_41$$19, "view:beforeRenderView"); - ZEPHIR_CALL_METHOD(&_40$$19, &eventsManager, "fire", &_42, 0, &_41$$19, this_ptr, &viewEnginePath); + ZEPHIR_INIT_NVAR(&_43$$21); + ZVAL_STRING(&_43$$21, "view:beforeRenderView"); + ZEPHIR_CALL_METHOD(&_42$$21, &eventsManager, "fire", &_44, 0, &_43$$21, this_ptr, &viewEnginePath); zephir_check_call_status(); - if (ZEPHIR_IS_FALSE_IDENTICAL(&_40$$19)) { + if (ZEPHIR_IS_FALSE_IDENTICAL(&_42$$21)) { continue; } } if (mustClean) { - ZVAL_BOOL(&_43$$15, 1); + ZVAL_BOOL(&_45$$17, 1); } else { - ZVAL_BOOL(&_43$$15, 0); + ZVAL_BOOL(&_45$$17, 0); } - ZEPHIR_CALL_METHOD(NULL, &engine, "render", NULL, 0, &viewEnginePath, params, &_43$$15); + ZEPHIR_CALL_METHOD(NULL, &engine, "render", NULL, 0, &viewEnginePath, params, &_45$$17); zephir_check_call_status(); notExists = 0; if (Z_TYPE_P(&eventsManager) == IS_OBJECT) { - ZEPHIR_INIT_NVAR(&_44$$21); - ZVAL_STRING(&_44$$21, "view:afterRenderView"); - ZEPHIR_CALL_METHOD(NULL, &eventsManager, "fire", &_45, 0, &_44$$21, this_ptr); + ZEPHIR_INIT_NVAR(&_46$$23); + ZVAL_STRING(&_46$$23, "view:afterRenderView"); + ZEPHIR_CALL_METHOD(NULL, &eventsManager, "fire", &_47, 0, &_46$$23, this_ptr); zephir_check_call_status(); } break; @@ -1071,18 +1084,18 @@ PHP_METHOD(Phalcon_Mvc_View_Simple, internalRender) ZEPHIR_INIT_NVAR(&engine); ZEPHIR_INIT_NVAR(&extension); if (notExists) { - ZEPHIR_INIT_VAR(&_46$$22); - object_init_ex(&_46$$22, phalcon_mvc_view_exceptions_simpleviewnotfound_ce); - ZEPHIR_CALL_METHOD(NULL, &_46$$22, "__construct", NULL, 0, &viewsDirPath); + ZEPHIR_INIT_VAR(&_48$$24); + object_init_ex(&_48$$24, phalcon_mvc_view_exceptions_simpleviewnotfound_ce); + ZEPHIR_CALL_METHOD(NULL, &_48$$24, "__construct", NULL, 0, &viewsDirPath); zephir_check_call_status(); - zephir_throw_exception_debug(&_46$$22, "phalcon/Mvc/View/Simple.zep", 525); + zephir_throw_exception_debug(&_48$$24, "phalcon/Mvc/View/Simple.zep", 537); ZEPHIR_MM_RESTORE(); return; } if (Z_TYPE_P(&eventsManager) == IS_OBJECT) { - ZEPHIR_INIT_VAR(&_47$$23); - ZVAL_STRING(&_47$$23, "view:afterRender"); - ZEPHIR_CALL_METHOD(NULL, &eventsManager, "fire", NULL, 0, &_47$$23, this_ptr); + ZEPHIR_INIT_VAR(&_49$$25); + ZVAL_STRING(&_49$$25, "view:afterRender"); + ZEPHIR_CALL_METHOD(NULL, &eventsManager, "fire", NULL, 0, &_49$$25, this_ptr); zephir_check_call_status(); } ZEPHIR_MM_RESTORE(); diff --git a/phalcon/Mvc/View.zep b/phalcon/Mvc/View.zep index 746e70bcd7..b13ef99353 100644 --- a/phalcon/Mvc/View.zep +++ b/phalcon/Mvc/View.zep @@ -604,16 +604,28 @@ class View extends Injectable implements ViewInterface, EventsAwareInterface * partial */ /** - * Drop "." and ".." path segments so a crafted partial path cannot + * Resolve "." and ".." path segments so a crafted partial path cannot * climb out of the partials directory, while still allowing - * sub-directories and absolute paths (CWE-22). Backslashes are - * separators on Windows, so they are normalized first. + * sub-directories and absolute paths (CWE-22). A ".." removes the + * previous segment. At the top level, or at the root of an absolute + * path, the ".." is dropped. Backslashes are separators on Windows, + * so they are normalized first. */ let segments = []; for segment in explode("/", str_replace("\\", "/", partialPath)) { - if segment !== "." && segment !== ".." { - let segments[] = segment; + if segment === "." { + continue; } + + if segment === ".." { + if count(segments) > 0 && segments[count(segments) - 1] !== "" { + array_pop(segments); + } + + continue; + } + + let segments[] = segment; } let partialPath = implode("/", segments); @@ -1298,7 +1310,8 @@ class View extends Injectable implements ViewInterface, EventsAwareInterface final protected function isAbsolutePath(string path) { if PHP_OS === "WINNT" { - return strlen(path) >= 3 && substr(path, 1, 2) === ":\\"; + return strlen(path) >= 3 + && (substr(path, 1, 2) === ":\\" || substr(path, 1, 2) === ":/"); } return str_starts_with(path, "/"); diff --git a/phalcon/Mvc/View/Simple.zep b/phalcon/Mvc/View/Simple.zep index c88ffcf840..cadf54ef04 100644 --- a/phalcon/Mvc/View/Simple.zep +++ b/phalcon/Mvc/View/Simple.zep @@ -458,16 +458,28 @@ class Simple extends Injectable implements ViewBaseInterface, EventsAwareInterfa mustClean = true; /** - * Drop "." and ".." path segments so a crafted view path cannot climb - * out of the views directory, while still allowing sub-directories and - * absolute paths (CWE-22). Backslashes are separators on Windows, so - * they are normalized first. + * Resolve "." and ".." path segments so a crafted view path cannot + * climb out of the views directory, while still allowing + * sub-directories and absolute paths (CWE-22). A ".." removes the + * previous segment. At the top level, or at the root of an absolute + * path, the ".." is dropped. Backslashes are separators on Windows, + * so they are normalized first. */ let segments = []; for segment in explode("/", str_replace("\\", "/", path)) { - if segment !== "." && segment !== ".." { - let segments[] = segment; + if segment === "." { + continue; } + + if segment === ".." { + if count(segments) > 0 && segments[count(segments) - 1] !== "" { + array_pop(segments); + } + + continue; + } + + let segments[] = segment; } let path = implode("/", segments); diff --git a/tests/unit/Mvc/View/PartialTest.php b/tests/unit/Mvc/View/PartialTest.php index d2dc802bd8..0f8ea67709 100755 --- a/tests/unit/Mvc/View/PartialTest.php +++ b/tests/unit/Mvc/View/PartialTest.php @@ -147,6 +147,34 @@ public function testMvcViewPartialCannotTraverseWithBackslash(): void $this->assertSame('Hey, this is a partial, also abcde', $actual); } + /** + * A `..` that stays inside the partial path removes the previous segment + * and is not dropped. + * + * @issue https://github.com/phalcon/cphalcon/issues/17606 + * @author Phalcon Team + * @since 2026-09-23 + */ + public function testMvcViewPartialParentSegment(): void + { + $container = new Di(); + $view = new View(); + + $view->setViewsDir( + $this->getDirSeparator(Talon::settings()->supportPath('assets/views')) + ); + $view->setDI($container); + + ob_start(); + $view->partial( + 'currentrender/../partials/partial', + ['cool_var' => 'abcde'] + ); + $actual = ob_get_clean(); + + $this->assertSame('Hey, this is a partial, also abcde', $actual); + } + /** * Dropping the `.` and `..` segments must keep a legitimate sub-directory * partial working. diff --git a/tests/unit/Mvc/View/Simple/RenderTraversalTest.php b/tests/unit/Mvc/View/Simple/RenderTraversalTest.php index b675fcf69e..966586bca3 100644 --- a/tests/unit/Mvc/View/Simple/RenderTraversalTest.php +++ b/tests/unit/Mvc/View/Simple/RenderTraversalTest.php @@ -90,4 +90,22 @@ public function testMvcViewSimpleRenderCannotTraverseWithBackslash(): void $this->assertSame($expected, $actual); } + + /** + * A `..` that stays inside the render path removes the previous segment + * and is not dropped. + * + * @issue https://github.com/phalcon/cphalcon/issues/17606 + * @author Phalcon Team + * @since 2026-09-23 + */ + public function testMvcViewSimpleRenderParentSegment(): void + { + $view = $this->container->get('viewSimple'); + + $expected = 'here'; + $actual = $view->render('partials/../currentrender/other'); + + $this->assertSame($expected, $actual); + } }