php7扩展开发[11] MVC之路由解析和加载文件

WBOY
Freigeben: 2016-06-23 13:09:30
Original
1205 Leute haben es durchsucht

```
场景:想要用C实现PHP的一个MVC结构的路由解析和加载文件的功能,一共要解决几个问题
1.由于MVC要加载多个C文件,所以要修正config.m4,修改config.m4内容第十行左右,去掉dnl,
PHP_ARG_WITH(dora, for route support,
dnl Make sure that the comment is aligned:
[  --with-route             Include dora support])
在下面追加到以下内容:
if test -z "$PHP_DEBUG" ; then
    AC_ARG_ENABLE(debug, [--enable-debug compile with debugging system], [PHP_DEBUG=$enableval],[PHP_DEBUG=no] )
fi

最后一行,加载所需所有C文件,如下:
  PHP_NEW_EXTENSION(dora, dora.c common/utilts.c loader/loader.c route/route.c controller/controller.c model/model.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
  PHP_ADD_BUILD_DIR([$ext_builddir/common])
  PHP_ADD_BUILD_DIR([$ext_builddir/loader])
  PHP_ADD_BUILD_DIR([$ext_builddir/route])
  PHP_ADD_BUILD_DIR([$ext_builddir/controller])
  PHP_ADD_BUILD_DIR([$ext_builddir/model])
 
```
```c

#include "utilts.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "Zend/zend_list.h"
#include "Zend/zend_interfaces.h"

//执行PHP文件函数
int zend_execute_scripts_ext(char *filepath){

    zval retval;

    zend_file_handle zfd;
    zfd.type = ZEND_HANDLE_FILENAME;
    zfd.filename = filepath;
    zfd.free_filename = 0;
    zfd.opened_path = NULL;

    //zend_execute_scripts(int type, zval *retval, int file_count, ...);
    //FAILURE OR SUCCESS
    return  zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC,&retval,1,&zfd);
    


}


//调用类中的方法
int call_user_class_method(zval *retval, zend_class_entry *obj_ce,
                           zval *obj, zval func,  uint32_t params_count, zval params[]){


    HashTable *function_table;

    if(obj) {
                function_table = &Z_OBJCE_P(obj)->function_table;
        }else{
                function_table = (CG(function_table));
    }

    zend_fcall_info fci;  
    fci.size = sizeof(fci);  
    fci.function_table = function_table;  
    fci.object =  obj ? Z_OBJ_P(obj) : NULL;;
    fci.function_name = func;   
    fci.retval = retval;  
    fci.param_count = params_count;  
    fci.params = params;  
    fci.no_separation = 1;  
    fci.symbol_table = NULL;  


 
    //FAILURE OR SUCCESS
    return  zend_call_function(&fci, NULL TSRMLS_CC);         //函数调用结束。  

}


```
```c
3.修改php_route.h头文件内容

在第五十行左右,加入以下内容

//定义类
extern zend_class_entry *route_ce;
//定义loader类中的方法
PHP_METHOD(route_ce,__construct);
PHP_METHOD(route_ce,run);
```
```c
4.修改route.c文件内容

/**
 * 声明构造函数
 * @param
 * @return
 */
ZEND_METHOD(route,__construct){

 
    zval *app_dir;

    if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &app_dir) == FAILURE )
    {
        RETURN_NULL();
    }
    //zend_update_static_property_stringl(zend_class_entry *scope, const char *name, size_t name_length, const char *value, size_t value_length);
    zend_update_static_property(route_ce, "app_dir", sizeof("app_dir")-1, app_dir TSRMLS_CC);

}



/**
 * 加载view
 * @param
 * @return
 */
ZEND_METHOD(route,run){


      zend_string* controller_name = zend_string_init("Index",strlen("Index"),0);
      zend_string* action_name     = zend_string_init("Index",strlen("Index"),0);

      zval *c_result;
      zval *a_result;
      int flag;



      //设置站点目录
      zval *app_dir = zend_read_static_property(Z_OBJCE_P(getThis()), "app_dir", sizeof("app_dir")-1, 0 TSRMLS_DC);


      //获取GET请求参数hashtable
      zval *get_arr = &PG(http_globals)[TRACK_VARS_GET];
      HashTable *ht= HASH_OF(get_arr);
      //int array_count = zend_hash_num_elements(Z_ARRVAL_P(get_arr));
    

      //获取controller_name
      zend_string *c_key= zend_string_init("controller", sizeof("controller")-1, 0);

      if ((c_result = zend_hash_find(ht, c_key)) != NULL) {
      
            controller_name = zval_get_string(c_result);
        
      }else{

            zend_error_noreturn(E_CORE_ERROR,  "Couldn't find controller param in url.");

      }
      //释放key的变量
      zend_string_release(c_key);


      //获取action_name
      zend_string *a_key= zend_string_init("action", sizeof("action")-1, 0);

      if ((a_result = zend_hash_find(ht, a_key)) != NULL) {

            action_name = zval_get_string(a_result);
            //php_printf("%s\n", Z_STRVAL_P(a_result));
            //php_printf("%s\n", zval_get_string(a_result));
      }else{

            zend_error_noreturn(E_CORE_ERROR,"Couldn't find action param in url.");

      }

      //释放key的变量
      zend_string_release(a_key);


      //拼装controller文件路径
      char *path = Z_STRVAL_P(app_dir);
      char *c_2 = "controllers/";
      strcat(path,c_2);

      //zend_string->char *
      char *c_3 = ZSTR_VAL(controller_name);
      strcat(path,c_3);

      char *c_4 = ".php";
      strcat(path,c_4);

      //php_printf("%s\n", c_1);
      // php_printf("%s\n", controller_name);
      // php_printf("%s\n", action_name);
      //PHPWRITE(Z_STRVAL_P(app_dir), Z_STRLEN_P(app_dir));



      //加载执行controller文件
      flag = zend_execute_scripts_ext(c_1);

      if(flag == FAILURE){

            zend_error_noreturn(E_CORE_ERROR,"Couldn't find file: %s.",c_1);

      }


      //查找controller对应的
      //zend_class_entry *zend_lookup_class(zend_string *name);
      zend_class_entry *controller_ce = zend_lookup_class(controller_name);

      if(controller_ce == NULL){

            zend_error_noreturn(E_CORE_ERROR,"Couldn't find file: %s.",c_1);
      }


      zval obj;
      object_init_ex(&obj, controller_ce);

      
      zval function_name;
      ZVAL_STRING(&function_name,ZSTR_VAL(action_name));

      
      flag = call_user_class_method(return_value, controller_ce, &obj, function_name, 0, NULL);

      if(flag == FAILURE){


            zend_error_noreturn(E_CORE_ERROR,
                                "Couldn't find implementation for method %s%s%s",
                                controller_ce ? ZSTR_VAL(controller_ce->name) : "",
                                controller_ce ? "::" : "",
                                function_name);
        
      }

      //RETURN_ZVAL(get_arr, 1, 0);

}

const zend_function_entry route_functions[] = {


    //注册route类中的方法
    ZEND_ME(route, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
    ZEND_ME(route,run,NULL,ZEND_ACC_PUBLIC)


    PHP_FE_END    /* Must be the last line in route_functions[] */
};

PHP_MINIT_FUNCTION(route)
{

    //注册route类
    zend_class_entry ce;

    //define INIT_NS_CLASS_ENTRY(class_container, ns, class_name, functions)
    INIT_NS_CLASS_ENTRY(ce,"Dora" ,"Route", route_functions);
    route_ce = zend_register_internal_class(&ce TSRMLS_CC);

    //声明一个静态数据成员app_dir
    //zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type);
    zend_declare_property_string(route_ce, "app_dir", strlen("app_dir"), "",ZEND_ACC_PUBLIC|ZEND_ACC_STATIC TSRMLS_DC);
    

    return SUCCESS;
}
```
```c
5.编译安装
phpize
./configure --with-php-config=/usr/bin/php-config
make && make install

```
```c
6.php7创建类所有到的知识点


常见的变量操作宏

CG    -> Complier Global      编译时信息,包括函数表等(zend_globals_macros.h:32)
EG    -> Executor Global      执行时信息(zend_globals_macros.h:43)
PG    -> PHP Core Global      主要存储php.ini中的信息
SG    -> SAPI Global          SAPI信息

//===============================================================================
PHP7中的zval的类型:
/* regular data types */
define IS_UNDEF                    0
define IS_NULL                     1
define IS_FALSE                    2
define IS_TRUE                     3
define IS_LONG                     4
define IS_DOUBLE                   5
define IS_STRING                   6
define IS_ARRAY                    7
define IS_OBJECT                   8
define IS_RESOURCE                 9
define IS_REFERENCE                10

define IS_CONSTANT                 11
define IS_CONSTANT_AST             12

define _IS_BOOL                    13
define IS_CALLABLE                 14

define IS_INDIRECT                 15
define IS_PTR                      17

//===============================================================================
PHP7中获取的zval赋值:
- ZVAL_STRING(zv, str, 1);
+ ZVAL_STRING(zv, str);
- ZVAL_STRINGL(zv, str, len, 1);
+ ZVAL_STRINGL(zv, str, len);
- ZVAL_STRING(zv, str, 0);
+ ZVAL_STRING(zv, str);
+ efree(str);
- ZVAL_STRINGL(zv, str, len, 0);
+ ZVAL_STRINGL(zv, str, len);

//===============================================================================

PHP7中获取的zval的值和长度:
define Z_LVAL(zval)                (zval).value.lval
define Z_LVAL_P(zval_p)            Z_LVAL(*(zval_p))

define Z_DVAL(zval)                (zval).value.dval
define Z_DVAL_P(zval_p)            Z_DVAL(*(zval_p))

define Z_STR(zval)                 (zval).value.str
define Z_STR_P(zval_p)             Z_STR(*(zval_p))

define Z_STRVAL(zval)              ZSTR_VAL(Z_STR(zval))
define Z_STRVAL_P(zval_p)          Z_STRVAL(*(zval_p))

define Z_STRLEN(zval)              ZSTR_LEN(Z_STR(zval))
define Z_STRLEN_P(zval_p)          Z_STRLEN(*(zval_p))

define Z_STRHASH(zval)             ZSTR_HASH(Z_STR(zval))
define Z_STRHASH_P(zval_p)         Z_STRHASH(*(zval_p))

define Z_ARR(zval)                 (zval).value.arr
define Z_ARR_P(zval_p)             Z_ARR(*(zval_p))

define Z_ARRVAL(zval)              Z_ARR(zval)
define Z_ARRVAL_P(zval_p)          Z_ARRVAL(*(zval_p))

define Z_OBJ(zval)                 (zval).value.obj
define Z_OBJ_P(zval_p)             Z_OBJ(*(zval_p))

define Z_OBJ_HT(zval)              Z_OBJ(zval)->handlers
define Z_OBJ_HT_P(zval_p)          Z_OBJ_HT(*(zval_p))

define Z_OBJ_HANDLER(zval, hf)     Z_OBJ_HT((zval))->hf
define Z_OBJ_HANDLER_P(zv_p, hf)   Z_OBJ_HANDLER(*(zv_p), hf)

define Z_OBJ_HANDLE(zval)          (Z_OBJ((zval)))->handle
define Z_OBJ_HANDLE_P(zval_p)      Z_OBJ_HANDLE(*(zval_p))

define Z_OBJCE(zval)               (Z_OBJ(zval)->ce)
define Z_OBJCE_P(zval_p)           Z_OBJCE(*(zval_p))

define Z_OBJPROP(zval)             Z_OBJ_HT((zval))->get_properties(&(zval))
define Z_OBJPROP_P(zval_p)         Z_OBJPROP(*(zval_p))

define Z_OBJDEBUG(zval,tmp)        (Z_OBJ_HANDLER((zval),get_debug_info)?Z_OBJ_HANDLER((zval),get_debug_info)(&(zval),&tmp):(tmp=0,Z_OBJ_HANDLER((zval),get_properties)?Z_OBJPROP(zval):NULL))
define Z_OBJDEBUG_P(zval_p,tmp)    Z_OBJDEBUG(*(zval_p), tmp)

define Z_RES(zval)                 (zval).value.res
define Z_RES_P(zval_p)             Z_RES(*zval_p)

define Z_RES_HANDLE(zval)          Z_RES(zval)->handle
define Z_RES_HANDLE_P(zval_p)      Z_RES_HANDLE(*zval_p)

define Z_RES_TYPE(zval)            Z_RES(zval)->type
define Z_RES_TYPE_P(zval_p)        Z_RES_TYPE(*zval_p)

define Z_RES_VAL(zval)             Z_RES(zval)->ptr
define Z_RES_VAL_P(zval_p)         Z_RES_VAL(*zval_p)

define Z_REF(zval)                 (zval).value.ref
define Z_REF_P(zval_p)             Z_REF(*(zval_p))

define Z_REFVAL(zval)              &Z_REF(zval)->val
define Z_REFVAL_P(zval_p)          Z_REFVAL(*(zval_p))

define Z_AST(zval)                 (zval).value.ast
define Z_AST_P(zval_p)             Z_AST(*(zval_p))

define Z_ASTVAL(zval)              (zval).value.ast->ast
define Z_ASTVAL_P(zval_p)          Z_ASTVAL(*(zval_p))

define Z_INDIRECT(zval)            (zval).value.zv
define Z_INDIRECT_P(zval_p)        Z_INDIRECT(*(zval_p))

define Z_CE(zval)                  (zval).value.ce
define Z_CE_P(zval_p)              Z_CE(*(zval_p))

define Z_FUNC(zval)                (zval).value.func
define Z_FUNC_P(zval_p)            Z_FUNC(*(zval_p))

define Z_PTR(zval)                 (zval).value.ptr
define Z_PTR_P(zval_p)             Z_PTR(*(zval_p))

//===============================================================================

php7 用来判断类型和取值

void display_value(zval zv,zval *zv_p,zval **zv_pp)
{
    if( Z_TYPE(zv) == IS_NULL )
    {
        php_printf("类型是 IS_NULL!\n");
    }
     
    if( Z_TYPE_P(zv_p) == IS_LONG )
    {
        php_printf("类型是 IS_LONG,值是:%ld" , Z_LVAL_P(zv_p));
    }
     
    if(Z_TYPE_PP(zv_pp) == IS_DOUBLE )
    {
        php_printf("类型是 IS_DOUBLE,值是:%f" , Z_DVAL_PP(zv_pp) );
    }
}

//================================================================================

PHP7中的定义返回值的宏 Zend/zend_API.h
define RETVAL_BOOL(b)                                  ZVAL_BOOL(return_value, b)
define RETVAL_NULL()                                   ZVAL_NULL(return_value)
define RETVAL_LONG(l)                                  ZVAL_LONG(return_value, l)
define RETVAL_DOUBLE(d)                                ZVAL_DOUBLE(return_value, d)
define RETVAL_STR(s)                                   ZVAL_STR(return_value, s)
define RETVAL_INTERNED_STR(s)                  ZVAL_INTERNED_STR(return_value, s)
define RETVAL_NEW_STR(s)                               ZVAL_NEW_STR(return_value, s)
define RETVAL_STR_COPY(s)                              ZVAL_STR_COPY(return_value, s)
define RETVAL_STRING(s)                                ZVAL_STRING(return_value, s)
define RETVAL_STRINGL(s, l)                    ZVAL_STRINGL(return_value, s, l)
define RETVAL_EMPTY_STRING()                   ZVAL_EMPTY_STRING(return_value)
define RETVAL_RES(r)                                   ZVAL_RES(return_value, r)
define RETVAL_ARR(r)                                   ZVAL_ARR(return_value, r)
define RETVAL_OBJ(r)                                   ZVAL_OBJ(return_value, r)
define RETVAL_ZVAL(zv, copy, dtor)             ZVAL_ZVAL(return_value, zv, copy, dtor)
define RETVAL_FALSE                                    ZVAL_FALSE(return_value)
define RETVAL_TRUE                                     ZVAL_TRUE(return_value)


define RETURN_BOOL(b)                                  { RETVAL_BOOL(b); return; }
define RETURN_NULL()                                   { RETVAL_NULL(); return;}
define RETURN_LONG(l)                                  { RETVAL_LONG(l); return; }
define RETURN_DOUBLE(d)                                { RETVAL_DOUBLE(d); return; }
define RETURN_STR(s)                                   { RETVAL_STR(s); return; }
define RETURN_INTERNED_STR(s)                  { RETVAL_INTERNED_STR(s); return; }
define RETURN_NEW_STR(s)                               { RETVAL_NEW_STR(s); return; }
define RETURN_STR_COPY(s)                              { RETVAL_STR_COPY(s); return; }
define RETURN_STRING(s)                                { RETVAL_STRING(s); return; }
define RETURN_STRINGL(s, l)                    { RETVAL_STRINGL(s, l); return; }
define RETURN_EMPTY_STRING()                   { RETVAL_EMPTY_STRING(); return; }
define RETURN_RES(r)                                   { RETVAL_RES(r); return; }
define RETURN_ARR(r)                                   { RETVAL_ARR(r); return; }
define RETURN_OBJ(r)                                   { RETVAL_OBJ(r); return; }
define RETURN_ZVAL(zv, copy, dtor)             { RETVAL_ZVAL(zv, copy, dtor); return; }
define RETURN_FALSE                                    { RETVAL_FALSE; return; }
define RETURN_TRUE                                     { RETVAL_TRUE; return; }
array_init(return_value);//初始化return_value成数组,此操作完后就可以返回一个空的数组
object_init(return_value);//初始化return_value成Object,此操作完成后返回一个空的对像




//===============================================================================
grep "define ZEND_ACC"  Zend/*.h
内核中提供了定义类以方法的修饰词 Zend/zend_compile.h声明定义

define ZEND_ACC_STATIC         0x01
define ZEND_ACC_ABSTRACT       0x02
define ZEND_ACC_FINAL          0x04
define ZEND_ACC_IMPLEMENTED_ABSTRACT       0x08
define ZEND_ACC_IMPLICIT_ABSTRACT_CLASS    0x10
define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS    0x20
define ZEND_ACC_INTERFACE                  0x40
define ZEND_ACC_TRAIT                      0x80
define ZEND_ACC_ANON_CLASS                 0x100
define ZEND_ACC_ANON_BOUND                 0x200

define ZEND_ACC_PUBLIC     0x100
define ZEND_ACC_PROTECTED  0x200
define ZEND_ACC_PRIVATE    0x400
define ZEND_ACC_PPP_MASK  (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE)
define ZEND_ACC_CHANGED    0x800
define ZEND_ACC_IMPLICIT_PUBLIC    0x1000
define ZEND_ACC_CTOR       0x2000
define ZEND_ACC_DTOR       0x4000
define ZEND_ACC_CLONE      0x8000


//===============================================================================
1. grep ZEND_ACC  Zend/*.h
内核中提供了定义类属性的宏  Zend/zend_API.h声明定义

ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment);
ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type);
ZEND_API int zend_declare_property_null(zend_class_entry *ce, const char *name, size_t name_length, int access_type);
ZEND_API int zend_declare_property_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_long value, int access_type);
ZEND_API int zend_declare_property_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value, int access_type);
ZEND_API int zend_declare_property_double(zend_class_entry *ce, const char *name, size_t name_length, double value, int access_type);
ZEND_API int zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type);
ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type);


更新类中的数据成员 Zend/zend_API.h声明定义

ZEND_API void zend_update_property_ex(zend_class_entry *scope, zval *object, zend_string *name, zval *value);
ZEND_API void zend_update_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zval *value);
ZEND_API void zend_update_property_null(zend_class_entry *scope, zval *object, const char *name, size_t name_length);
ZEND_API void zend_update_property_bool(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_long value);
ZEND_API void zend_update_property_long(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_long value);
ZEND_API void zend_update_property_double(zend_class_entry *scope, zval *object, const char *name, size_t name_length, double value);
ZEND_API void zend_update_property_str(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_string *value);
ZEND_API void zend_update_property_string(zend_class_entry *scope, zval *object, const char *name, size_t name_length, const char *value);
ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zval *object, const char *name, size_t name_length, const char *value, size_t value_length);

grep "zend_read_"  ../../Zend/*.h
读取类中的数据成员 在Zend/zend_API.h声明定义

ZEND_API zval *zend_read_property(zend_class_entry *scope, zval *object, const char *name, size_t name_length, zend_bool silent, zval *rv);



//===============================================================================
2. grep "zend_declare_class_constant"  Zend/*.h
创建类中的常量的方法在Zend/zend_API.h声明定义

ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value);
ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length);
ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value);
ZEND_API int zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_bool value);
ZEND_API int zend_declare_class_constant_double(zend_class_entry *ce, const char *name, size_t name_length, double value);
ZEND_API int zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length);
ZEND_API int zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value);

更新类中的常量数据成员 Zend/zend_API.h声明定义
ZEND_API int zend_update_class_constants(zend_class_entry *class_type);



//=================================================================================
3. grep "zend_update_static_"  ../../Zend/*.h
更新类中的静态数据成员 在Zend/zend_API.

ZEND_API int zend_update_static_property(zend_class_entry *scope, const char *name, size_t name_length, zval *value);
ZEND_API int zend_update_static_property_null(zend_class_entry *scope, const char *name, size_t name_length);
ZEND_API int zend_update_static_property_bool(zend_class_entry *scope, const char *name, size_t name_length, zend_long value);
ZEND_API int zend_update_static_property_long(zend_class_entry *scope, const char *name, size_t name_length, zend_long value);
ZEND_API int zend_update_static_property_double(zend_class_entry *scope, const char *name, size_t name_length, double value);
ZEND_API int zend_update_static_property_string(zend_class_entry *scope, const char *name, size_t name_length, const char *value);
ZEND_API int zend_update_static_property_stringl(zend_class_entry *scope, const char *name, size_t name_length, const char *value, size_t value_length);


grep "zend_read_"  ../../Zend/*.h
读取类中的数据成员 在Zend/zend_API.h声明定义

ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, zend_bool silent);


```

- 请尊重本人劳动成功,可以随意转载但保留以下信息
- 作者:岁月经年
- 时间:2016年04月

Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage