찾다
백엔드 개발PHP 튜토리얼深入理解PHP内核(三)概览-SAPI概述,深入理解-sapi_PHP教程

深入理解PHP内核(三)概览-SAPI概述,深入理解-sapi

 本文链接:http://www.orlion.ml/234/

1、在PHP生命周期的各个阶段,一些与服务相关的操作都是通过SAPI接口实现。这些内置实现的物理位置在PHP源码的SAPI目录。这个目录存放了PHP对各个服务器抽象层的代码,例如命令行程序的实现,Apache的mod_php模块实现以及fastcgi的实现等等

在各个服务器抽象层之间遵守着相同的约定,这里我们称之为SAPI接口。每个SAPI实现都是一个_sapi_module_struct结构体变量。(SAPI接口)。在PHP的源码中,当需要调用服务器相关信息时,全部通过SAPI接口中对应的方法调用实现,而这些方法在各个服务器抽象层实现时都会有各自的实现。由于很多操作的通用性,有很大一部分接口方法使用的是默认方法。下图为SPAI的简单示意图

以cgi模式和apache2服务器为例,它们的启动方法如下:

cgi_sapi_module.startup(&cgi_sapi_module) // cgi模式 cgi/cgi_main.c文件
<span>
apache_sapi_module.startup(&apache_sapi_module); // apache服务器  apache2handler/sapi_apache2.c文件</span>

这里的cgi_sapi_module是sapi_module_struct结构体的静态变量。它的startup方法指向php_cgi_startup函数指针。在这个结构体中除了startup函数指针,还有许多其他方法或字段,这些结构在服务器的接口实现中都有定义

 

整个SAPI类似于一个面向对象中的模板方法模式的应用。SAPI.c和SAPI.h文件所包含的一些函数就是模板方法模式中的抽象模板,各个服务器对于sapi_module的定义及相关实现则是一个个具体的模板

 

2、Apache模块

(1)当PHP需要在Apache服务器下运行时,一般来说,它可以mod_php5模块的形式集成,此时mod_php5模块的作用是接收Aapche传递过来的PHP文件请求,并处理这些请求,然后将处理后的结果返回给Apache。如果我们在Apache启动前在其配置文件中配置了PHP模块,PHP模块通过注册apache2的ap_hook_post_config挂钩,在Apache启动的时候启动此模块以接收PHP文件的请求。

除了这种启动时的加载方式,Apache的模块可以在运行的时候动态装载,这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译,甚至不需要重启服务器。我们所需要做的仅仅是给服务器发送信号HUP或者AP_SIG_GEACEFUL通知服务器重新载入模块。但是在动态装载之前我们需要将模块编译成为动态链接库。此时的动态加载就是加载动态链接库。Apache中对动态链接库的处理是通过模块mod_so来完成的,因此mod_so模块不能被动态加载,它只能本静态编译进Apache的核心。这意味着它和Apache一起启动的。

 

Apache是如何加载模块的呢?以mod_php5为例,首先在httpd.conf中添加一行:

LoadModule php5_module modules/mod_php5.so

在配置文件中添加了所示的指令后,Apache在加载模块时会根据模块名查找模块并加载。Apache的每一个模块都是以module结构体的形式存在,module结构的name属性在最后是通过宏STANDARD20_MODULE_STUFF以__FILE__体现。通过之前的指令中指定的路径找到相关的动态链接库文件后,Apache通过内部的函数获取动态链接库中的内容,并将模块的内容加载到内存中指定变量中。

在真正激活模块之前,Apache会检查所有加载的模块是否为真正的Apache模块。最后Apache会调用相关的函数(ap_add_loaded_module)将模块激活,此处的激活就是将模块放入相应的链表中(ap_top_modules链表)

Apache加载的是PHP模块,那么这个模块时怎么实现的呢?Apache2的mod_php5模块包括sapi/apache2handler和sapi/apache2filter两个目录,在apache2_handle/mod_php5.c文件中,模块定义的相关代码如下:

AP_MODULE_DECLARE_DATA module php5_module =<span> {
    STANDARD20_MODULE_STUFF,
        /* 宏,包括版本,小版本,模块索引,模块名,下一个模块指针等信息,其中模块名以__FILE__体现*/<span>
    create_php_config,      /* create per-directory config structure */<span>
    merge_php_config,       /* merge per-directory config structures */<span>
    NULL,                   /* create per-server config structure */<span>
    NULL,                   /* merge per-server config structures */<span>
    php_dir_cmds,           /*模块定义的所有命令*/<span>
    php_ap2_register_hook  /*注册钩子,此函数通过ap_hoo_开头的函数在一次处理过程中对于指定的步骤注册钩子*/<span>
};</span></span></span></span></span></span></span></span>

它所对应的是Apache的module结构,module的结构定义如下:

typedef struct<span> module_struct module;
struct<span> module_struct {
    int<span> version;
    int<span> minor_version;
    int<span> module_index;
    const char *<span>name;
    void *<span>dynamic_load_handle;
    struct module_struct *<span>next;
    unsigned long<span> magic;
    void (*rewrite_args) (process_rec *<span>process);
    void *(*create_dir_config) (apr_pool_t *p, char *<span>dir);
    void *(*merge_dir_config) (apr_pool_t *p, void *base_conf, void *<span>new_conf);
    void *(*create_server_config) (apr_pool_t *p, server_rec *<span>s);
    void *(*merge_server_config) (apr_pool_t *p, void *base_conf, void 
*<span>new_conf);
    const command_rec *<span>cmds;
    void (*register_hooks) (apr_pool_t *<span>p);
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

 

上面的模块结构与我们在mod_php5.c中所看到的结构有一点不同,这是由于STANDARD20_MODULE_STUFF的原因,这个宏它包含了前面8个字段的定义。STANDARD20_MODULE_STUFF宏的定义如下:

/** Use this in all standard modules */
#define STANDARD20_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \<span>
                MODULE_MAGIC_NUMBER_MINOR, \
                -1<span>, \
                __FILE__, \
                NULL, \
                NULL, \
                MODULE_MAGIC_COOKIE, \
                                NULL      /* rewrite args spot */</span></span>

在php5_module定义的结构中,php_dir_cmds是模块定义的所有的指令集合,定义的内容如下:

const command_rec php_dir_cmds[] =<span>
{
    AP_INIT_TAKE2("php_value"<span>, php_apache_value_handler, NULL,
        OR_OPTIONS, "PHP Value Modifier"<span>),
    AP_INIT_TAKE2("php_flag"<span>, php_apache_flag_handler, NULL,
        OR_OPTIONS, "PHP Flag Modifier"<span>),
    AP_INIT_TAKE2("php_admin_value"<span>, php_apache_admin_value_handler,
        NULL, ACCESS_CONF|RSRC_CONF, "PHP Value Modifier (Admin)"<span>),
    AP_INIT_TAKE2("php_admin_flag"<span>, php_apache_admin_flag_handler,
        NULL, ACCESS_CONF|RSRC_CONF, "PHP Flag Modifier (Admin)"<span>),
    AP_INIT_TAKE1("PHPINIDir"<span>, php_apache_phpini_set, NULL,
        RSRC_CONF, "Directory containing the php.ini file"<span>),
    {NULL}
};</span></span></span></span></span></span></span></span></span></span></span>

 

这是mod_php5模块定义的指令表。它实际上是一个commond_rec结构的数组。当Apache遇到指令的时候将逐一遍历各个模块中的指令表,查找是否有那个模块能够处理该指令,如果找到,则调用响应的处理函数,如果所有指令表中的模块都不能处理该指令,那么将报错,如上所见,mod_php5模块仅提供php_value等5个指令。

php_ap2_register_hook函数的定义如下:

void php_ap2_register_hook(apr_pool_t *<span>p)
{
    ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_config(php_apache_server_startup, NULL, NULL, 
APR_HOOK_MIDDLE);
    ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}</span>

以上代码声明了pre_config,post_config,handler和child_init4个挂钩以及对应的处理函数。其中pre_config,post_config,child_init是启动挂钩,它们在服务器启动时调用。handler挂钩是请求挂钩,它在服务器处理请求时调用。其中在post_config挂钩中启动php。它通过php_apache_server_startup函数实现,php_apache_server_startup函数通过调用sapi_startup启动sapi,并通过调用php_apache2_startup来注册sapi module struct,最后调用php_module_startup初始化php,其中又会初始化Zend引擎,以及填充zend_module_struct中的treat_data成员(通过php_startup_sapi_content_types)等。

  到这里,我们知道了Apache加载mod_php5模块的整个过程,可是这个过程与我们的饿SAPI有什么关系呢?mod_php5也定义了属于Apache的sapi_module_struct结构:

static sapi_module_struct apache2_sapi_module =<span> {
"apache2handler"<span>,
"Apache 2.0 Handler"<span>,
 
php_apache2_startup,                /* startup */<span>
php_module_shutdown_wrapper,            /* shutdown */<span>
 
NULL,                       /* activate */<span>
NULL,                       /* deactivate */<span>
 
php_apache_sapi_ub_write,           /* unbuffered write */<span>
php_apache_sapi_flush,              /* flush */<span>
php_apache_sapi_get_stat,           /* get uid */<span>
php_apache_sapi_getenv,             /* getenv */<span>
php_error,                  /* error handler */<span>
 
php_apache_sapi_header_handler,         /* header handler */<span>
php_apache_sapi_send_headers,           /* send headers handler */<span>
NULL,                       /* send header handler */<span>
 
php_apache_sapi_read_post,          /* read POST data */<span>
php_apache_sapi_read_cookies,           /* read Cookies */<span>
 
php_apache_sapi_register_variables,
php_apache_sapi_log_message,            /* Log message */<span>
php_apache_sapi_get_request_time,       /* Request Time */<span>
NULL,                       /* Child Terminate */<span>
 
STANDARD_SAPI_MODULE_PROPERTIES
};</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

 

这些方法都属于Apache服务器,以读取cookie为例,当我们在Apache服务器环境下,在PHP中调用读取Cookie时,最终获取的数据的位置是在激活SAPI时,它所调用的方法是read_cookie。

SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C);

对于每一个服务器在加载时,我们都指定了sapi_module,而Apache的sapi_module是apache2_sapi_module。其中对应read_cookie的方法是php_apache_sapi_read_cookie函数。这也是定义SAPI结构的理由:统一接口,面向接口编程,具有更好的扩展性和适应性。

(2)Apache的运行过程

Apache的运行包括启动阶段和运行阶段,启动阶段Apache以root完成启动,整个过程处于单进程单线程的环境中,这个阶段包括配置文件解析、模块加载、系统资源初始化(例如日志文件、共享内存段、数据库连接等)等工作。

在运行阶段,Apache主要工作是处理用户的服务请求,在这个阶段Apache以普通用户运行。主要是安全性考虑,Apache对HTTP的请求可以分为连接、处理和断开连接三个大的阶段。

2、FastCGI

(1)cgi是通用网关接口(Common Gateway Intedface),它可以让一个客户端从网页浏览器向执行在Web服务器上的程序请求数据。CGI描述了客户端和这个程序之间传输数据的标准。CGI的一个目的是独立于任何语言,所以CGI可以用任何语言编写,只要这种语言具有标准输入、输出和环境变量。如PHP、perl、tcl等。

FastCGI是Web服务器和处理程序之间通信的一种协议,是CGI的一种改进方案,FastCGI像是一个常驻型的CGI,它可以一直执行,在请求到达时不会花费时间去fork一个进程来处理(这是CGI对位人诟病的fork-and-execute模式)。正是因为它只是一个通信协议,它还支持分布式的运算,即FastCGI程序可以在网站服务器以外的主机上执行并且接受来自其他网站服务器的请求

FastCGI的整个流程是这样的:

  Step1:Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)

  Step2:FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自web server的连接

  Step3:当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web Server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi

  Step4:FastCGI子进程完成处理后将标准输出和错误新词从同一连接返回Web Server 当FastCGI子进程关闭连接时,请求便结束。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。在CGI模式中,php-cgi在此便退出了。

 

(2)php中CGI实现

PHP的CGI实现了Fastcgi协议。是一个TCP或UDP协议的服务器接受来自Web服务器的请求,当启动时创建TCP/UDP协议的服务器的socket监听,并接受相关请求并进行处理。随后就进入了PHP的生命周期:模块初始化,sapi初始化,处理PHP请求,模块关闭,sapi关闭等 就构成了整个CGI的生命周期。

以TCP为例在,在TCP的服务端,一般会执行这样几个步骤:

1、调用socket函数创建一个TCP用的流式套接字;

2、调用bind函数将服务器的本地地址与前面创建的套接字绑定;

3、调用listen函数将新创建的套接字作为监听,等待客户端发起的连接,当客户端有多个连接连接到这个套接字时,可能需要排队处理;

4、服务器进程调用accept函数进入阻塞状态,直到有客户进程调用connect函数而建立起一个连接;

5、当与客户端创建连接后,服务器调用read_stream函数读取客户端的请求;

6、处理完数据后,服务器调用write函数向客户端发送应答

TCP上客户-服务器事务的时序如图所示:

php的CGI实现从cgi_main.c文件的main函数开始,在main函数中调用了定义在fastcgi.c文件中的初始化,监听等函数。对比TCP的流程,我们查看php对TCP协议的实现,虽然php本身也实现了这些流程,但是在main函数中一些过程被封装成一个函数实现。对应TCP的操作流程,PHP首先会执行创建socket,绑定套接字,创建监听:

if<span> (bindpath) {
    fcgi_fd = fcgi_listen(bindpath, 128);   //  socket˥˦2sfcgi_initɩ    
<span>Ȑ
    ...
}</span></span>

在fastcgi.c文件中,fcig_listen函数主要用于创建、绑定socket并开始监听,它走完了前面所列TCP流程的前三个阶段,

 if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||<span>
        ...
        bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||<span>
        listen(listen_socket, backlog) < 0<span>) {
        ...
    }</span></span></span>

当服务端初始化完成后,进程调用accept函数进入阻塞状态,在main函数中我们看到如下代码:

  while<span> (parent) {
        do<span> {
            pid = fork();   //  o&Ograve;Ÿ
<span>ȨėJ
            switch<span> (pid) {
            case 0: //  ȨėJ
                parent = 0<span>;
 
                /* don't catch our signals */<span>
                sigaction(SIGTERM, &old_term, 0);   //  ľ&acirc;&macr;ķ
                sigaction(SIGQUIT, &old_quit, 0);   //  ľĿɰ&pound;ƺ
                sigaction(SIGINT,  &old_int,  0);   //  ľĿKȠƺ
                break<span>;
                ...
                default<span>:
                /* Fine */<span>
                running++<span>;
                break<span>;
        } while (parent && (running <<span> children));
 
    ...
        while (!fastcgi || fcgi_accept_request(&request) >= 0<span>) {
        SG(server_context) = (void *) &<span>request;
        init_request_info(TSRMLS_C);
        CG(interactive) = 0<span>;
                    ...
            }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

如上的代码是一个生成子进程,并等待用户请求。在fcgi_accept_request函数中,程序会调用accept函数阻塞新创建的线程。当用户的请求到达时,fcgi_accept_request函数会判断是否处理用户的请求,其中会过滤某些连接请求,忽略受限制客户的请求,如果程序受理用户的请求,他将分析请求的信息,将相关的变量写到对应的变量中。其中在读取请求内容时调用了safe_read方法。如下所示:main()->fcgi_accept_request()->fcgi_read_request()->safe_read()

static inline ssize_t safe_read(fcgi_request *req, const void *<span>buf, size_t 
count)
{
    size_t n = 0<span>;
    do<span> {
    ... //  省略 对win32的处理<span>
        ret = read(req->fd, ((char*)buf)+n, count-n);   //  非win版本的读操作
<span>D‰
    ... // 省略
    } while (n !=<span> count);
 
}</span></span></span></span></span></span>

如上对应服务器端读取用户的请求数据。

在请求初始化完成,读取请求完毕后,就该处理请求的PHP文件了。假设此次请求为PHP_MODE_STANDARD则会调用php_execute_script执行PHP文件。在此函数中它先初始化此文件相关的一些内容,然后再调用zend_execute_scripts函数,对PHP文件进行词法分析和语法分析,生成中间代码,并执行zend_execute函数,从而执行这些中间代码。

  在处理完用户的请求后,服务端将返回信息给客户端,此时在main函数中调用的是fcgi_finish_request(&request , 1);fcgi_finish_request函数定义在fasftcgi.c文件中。

  在发送了请求的应答后,服务器端将会执行关闭操作,仅限于CGI本身的关闭,程序执行的是fcgi_close函数。

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1095476.htmlTechArticle深入理解PHP内核(三)概览-SAPI概述,深入理解-sapi 本文链接:http://www.orlion.ml/234/ 1、在PHP生命周期的各个阶段,一些与服务相关的操作都是通...
성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
PHP의 현재 상태 : 웹 개발 동향을 살펴보십시오PHP의 현재 상태 : 웹 개발 동향을 살펴보십시오Apr 13, 2025 am 12:20 AM

PHP는 현대 웹 개발, 특히 컨텐츠 관리 및 전자 상거래 플랫폼에서 중요합니다. 1) PHP는 Laravel 및 Symfony와 같은 풍부한 생태계와 강력한 프레임 워크 지원을 가지고 있습니다. 2) Opcache 및 Nginx를 통해 성능 최적화를 달성 할 수 있습니다. 3) PHP8.0은 성능을 향상시키기 위해 JIT 컴파일러를 소개합니다. 4) 클라우드 네이티브 애플리케이션은 Docker 및 Kubernetes를 통해 배포되어 유연성과 확장 성을 향상시킵니다.

PHP 대 기타 언어 : 비교PHP 대 기타 언어 : 비교Apr 13, 2025 am 12:19 AM

PHP는 특히 빠른 개발 및 동적 컨텐츠를 처리하는 데 웹 개발에 적합하지만 데이터 과학 및 엔터프라이즈 수준의 애플리케이션에는 적합하지 않습니다. Python과 비교할 때 PHP는 웹 개발에 더 많은 장점이 있지만 데이터 과학 분야에서는 Python만큼 좋지 않습니다. Java와 비교할 때 PHP는 엔터프라이즈 레벨 애플리케이션에서 더 나빠지지만 웹 개발에서는 더 유연합니다. JavaScript와 비교할 때 PHP는 백엔드 개발에서 더 간결하지만 프론트 엔드 개발에서는 JavaScript만큼 좋지 않습니다.

PHP vs. Python : 핵심 기능 및 기능PHP vs. Python : 핵심 기능 및 기능Apr 13, 2025 am 12:16 AM

PHP와 Python은 각각 고유 한 장점이 있으며 다양한 시나리오에 적합합니다. 1.PHP는 웹 개발에 적합하며 내장 웹 서버 및 풍부한 기능 라이브러리를 제공합니다. 2. Python은 간결한 구문과 강력한 표준 라이브러리가있는 데이터 과학 및 기계 학습에 적합합니다. 선택할 때 프로젝트 요구 사항에 따라 결정해야합니다.

PHP : 웹 개발의 핵심 언어PHP : 웹 개발의 핵심 언어Apr 13, 2025 am 12:08 AM

PHP는 서버 측에서 널리 사용되는 스크립팅 언어이며 특히 웹 개발에 적합합니다. 1.PHP는 HTML을 포함하고 HTTP 요청 및 응답을 처리 할 수 ​​있으며 다양한 데이터베이스를 지원할 수 있습니다. 2.PHP는 강력한 커뮤니티 지원 및 오픈 소스 리소스를 통해 동적 웹 컨텐츠, 프로세스 양식 데이터, 액세스 데이터베이스 등을 생성하는 데 사용됩니다. 3. PHP는 해석 된 언어이며, 실행 프로세스에는 어휘 분석, 문법 분석, 편집 및 실행이 포함됩니다. 4. PHP는 사용자 등록 시스템과 같은 고급 응용 프로그램을 위해 MySQL과 결합 할 수 있습니다. 5. PHP를 디버깅 할 때 error_reporting () 및 var_dump ()와 같은 함수를 사용할 수 있습니다. 6. 캐싱 메커니즘을 사용하여 PHP 코드를 최적화하고 데이터베이스 쿼리를 최적화하며 내장 기능을 사용하십시오. 7

PHP : 많은 웹 사이트의 기초PHP : 많은 웹 사이트의 기초Apr 13, 2025 am 12:07 AM

PHP가 많은 웹 사이트에서 선호되는 기술 스택 인 이유에는 사용 편의성, 강력한 커뮤니티 지원 및 광범위한 사용이 포함됩니다. 1) 배우고 사용하기 쉽고 초보자에게 적합합니다. 2) 거대한 개발자 커뮤니티와 풍부한 자원이 있습니다. 3) WordPress, Drupal 및 기타 플랫폼에서 널리 사용됩니다. 4) 웹 서버와 밀접하게 통합하여 개발 배포를 단순화합니다.

과대 광고 : 오늘 PHP의 역할을 평가합니다과대 광고 : 오늘 PHP의 역할을 평가합니다Apr 12, 2025 am 12:17 AM

PHP는 현대적인 프로그래밍, 특히 웹 개발 분야에서 강력하고 널리 사용되는 도구로 남아 있습니다. 1) PHP는 사용하기 쉽고 데이터베이스와 완벽하게 통합되며 많은 개발자에게 가장 먼저 선택됩니다. 2) 동적 컨텐츠 생성 및 객체 지향 프로그래밍을 지원하여 웹 사이트를 신속하게 작성하고 유지 관리하는 데 적합합니다. 3) 데이터베이스 쿼리를 캐싱하고 최적화함으로써 PHP의 성능을 향상시킬 수 있으며, 광범위한 커뮤니티와 풍부한 생태계는 오늘날의 기술 스택에 여전히 중요합니다.

PHP의 약한 참고 자료는 무엇이며 언제 유용합니까?PHP의 약한 참고 자료는 무엇이며 언제 유용합니까?Apr 12, 2025 am 12:13 AM

PHP에서는 약한 참조가 약한 회의 클래스를 통해 구현되며 쓰레기 수집가가 물체를 되 찾는 것을 방해하지 않습니다. 약한 참조는 캐싱 시스템 및 이벤트 리스너와 같은 시나리오에 적합합니다. 물체의 생존을 보장 할 수 없으며 쓰레기 수집이 지연 될 수 있음에 주목해야합니다.

PHP의 __invoke 마법 방법을 설명하십시오.PHP의 __invoke 마법 방법을 설명하십시오.Apr 12, 2025 am 12:07 AM

\ _ \ _ 호출 메소드를 사용하면 객체를 함수처럼 호출 할 수 있습니다. 1. 객체를 호출 할 수 있도록 메소드를 호출하는 \ _ \ _ 정의하십시오. 2. $ obj (...) 구문을 사용할 때 PHP는 \ _ \ _ invoke 메소드를 실행합니다. 3. 로깅 및 계산기, 코드 유연성 및 가독성 향상과 같은 시나리오에 적합합니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

맨티스BT

맨티스BT

Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경