0

0

关于PHP源码中HashTable的解析

不言

不言

发布时间:2018-06-28 16:12:51

|

1875人浏览过

|

来源于php中文网

原创

这篇文章主要介绍了关于php源码中hashtable的解析,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

PHP源码中HashTable的简单示例 前些日子看了那篇对hasttable的介绍,于是也想自己运行一下,可是对于源码的调试不是太在行。 所以想了个办法:自己把PHP源码中的一些简单操作提取出来,自己运行一下,查看输出或调试。 于是花费了三天的空闲时间把一些相关的东西提取出来,主要是Zend目录下的zend_alloc.c,zend_alloc.h,zend_hash.c,zend_hash.h四个文件。 将与PHP相关的内存分配去掉,默认使用系统自带的内存分配方式。 另外:一些注释是http://www.phppan.com/2009/12/zend-hashtable/中所引用文章中的相关信息。 作者地址:http://www.phpinternals.com 下面的代码是一个可以运行的C程序,它初始化一个容量为50的hashtable(实际上分配了64个),然后将30到68,写入hash table,并将这个hash table 打印出来。 相信这会给一些想学习源码的童鞋一些帮助。 源代码如下:

  #include #include typedef unsigned long ulong;typedef unsigned int uint;typedef unsigned char zend_bool;typedef unsigned int size_t;typedef void (*dtor_func_t)(void *pDest);typedef ulong (*hash_func_t)(char *arKey, uint nKeyLength);#define SUCCESS 0#define FAILURE -1 /* this MUST stay a negative number, or it may affect functions! */ #define HASH_UPDATE (1zuojiankuohaophpcnzuojiankuohaophpcn0)#define HASH_ADD (1zuojiankuohaophpcnzuojiankuohaophpcn1)#define HASH_NEXT_INSERT(1zuojiankuohaophpcnzuojiankuohaophpcn2) #define HASH_DEL_KEY 0 #define perealloc_recoverable(ptr, size, persistent) (__zend_realloc((ptr), (size)))#define pefree_rel(ptr, persistent)(free(ptr))//此处省略了使用PHP的内存分配函数#define pemalloc_rel(size, persistent) (__zend_malloc(size))#define perealloc_rel(ptr, size, persistent) (__zend_realloc((ptr), (size)))#define pemalloc(size, persistent) (__zend_malloc(size))#define pefree(ptr, persistent)  (free(ptr)) inline static void * __zend_malloc(size_t len) {
    void *tmp = malloc(len);
    if (tmp) {
        return tmp;
    }  
    fprintf(stderr, "Out of memory\n");
    exit(1);} 
 inline static void * __zend_realloc(void *p, size_t len) {
    p = realloc(p, len);
    if (p) {
        return p;
    }  
    fprintf(stderr, "Out of memory\n");
    exit(1);} 
 typedef struct bucket {
    ulong h;       /* Used for numeric indexing */
    uint nKeyLength;     /* key 长度 */
    void *pData;      /* 指向Bucket中保存的数据的指针 */
    void *pDataPtr;     /* 指针数据 */
    struct bucket *pListNext;   /* 指向HashTable桶列中下一个元素 */
    struct bucket *pListLast;    /* 指向HashTable桶列中前一个元素 */
    struct bucket *pNext;    /* 指向具有同一个hash值的桶列的后一个元素 */
    struct bucket *pLast;    /* 指向具有同一个hash值的桶列的前一个元素 */
    char arKey[1];      /* 必须是最后一个成员,key名称*/} Bucket; 
 typedef struct _hashtable {
    uint nTableSize;/*指定了HashTable的大小,同时它限定了HashTable中能保存Bucket的最大数量
此 数越大,系统为HashTable分配的内存就越多。为了提高计算效率,
系统自动会将nTableSize调整到最小一个不小于nTableSize的2 的整数次方*/
    uint nTableMask;/*nTableMask的值永远是nTableSize – 1,引入这个字段的主要目的是为了提高计算效率*/
    uint nNumOfElements;/*记录HashTable当前保存的数据元素的个数*/
    ulong nNextFreeElement;/*记录HashTable中下一个可用于插入数据元素的arBuckets的索引*/
    Bucket *pInternalPointer;/* Used for element traversal */
    Bucket *pListHead;/*Bucket双向链表的第一个元素*/
    Bucket *pListTail;/*Bucket双向链表的最后一元素*/
    Bucket **arBuckets;/*存储Bucket双向链表*/
    dtor_func_t pDestructor;/*函数指针,在HashTable的增加、修改、删除Bucket时自动调用,用于处理相关数据的清理工作*/
    zend_bool persistent;/*指出了Bucket内存分配的方式。如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数。*/
    unsigned char nApplyCount;/*nApplyCount与bApplyProtection结合提供了一个防止在遍历HashTable时进入递归循环时的一种机制*/
    zend_bool bApplyProtection;} HashTable; 
 
 typedef struct _zend_hash_key {
    char *arKey;
    uint nKeyLength;
    ulong h;} zend_hash_key; typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, void *source_data, zend_hash_key *hash_key, void *pParam); 
 #define CONNECT_TO_BUCKET_DLLIST(element, list_head) \
(element)-youjiankuohaophpcnpNext = (list_head); \
(element)-youjiankuohaophpcnpLast = NULL; \
if ((element)-youjiankuohaophpcnpNext) { \
    (element)-youjiankuohaophpcnpNext-youjiankuohaophpcnpLast = (element); \
} #define CONNECT_TO_GLOBAL_DLLIST(element, ht) \
(element)-youjiankuohaophpcnpListLast = (ht)-youjiankuohaophpcnpListTail; \
(ht)-youjiankuohaophpcnpListTail = (element); \
(element)-youjiankuohaophpcnpListNext = NULL; \
if ((element)-youjiankuohaophpcnpListLast != NULL) { \
    (element)-youjiankuohaophpcnpListLast-youjiankuohaophpcnpListNext = (element); \
} \
if (!(ht)-youjiankuohaophpcnpListHead) { \
    (ht)-youjiankuohaophpcnpListHead = (element); \
} \
if ((ht)-youjiankuohaophpcnpInternalPointer == NULL) { \
    (ht)-youjiankuohaophpcnpInternalPointer = (element); \
} #define ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
if ((ht)-youjiankuohaophpcnnNumOfElements youjiankuohaophpcn (ht)-youjiankuohaophpcnnTableSize) {\
    zend_hash_do_resize(ht); \
} int zend_hash_rehash(HashTable *ht) {
    Bucket *p;
    uint nIndex; 
     memset(ht-youjiankuohaophpcnarBuckets, 0, ht-youjiankuohaophpcnnTableSize * sizeof(Bucket *));
    p = ht-youjiankuohaophpcnpListHead;
    while (p != NULL) {
        nIndex = p-youjiankuohaophpcnh & ht-youjiankuohaophpcnnTableMask;
        CONNECT_TO_BUCKET_DLLIST(p, ht-youjiankuohaophpcnarBuckets[nIndex]);
        ht-youjiankuohaophpcnarBuckets[nIndex] = p;
        p = p-youjiankuohaophpcnpListNext;
    }
    return SUCCESS;} static int zend_hash_do_resize(HashTable *ht) {
    Bucket **t;     if ((ht-youjiankuohaophpcnnTableSize zuojiankuohaophpcnzuojiankuohaophpcn 1) youjiankuohaophpcn 0) {/* Let's double the table size */
        t = (Bucket **) perealloc_recoverable(ht-youjiankuohaophpcnarBuckets, (ht-youjiankuohaophpcnnTableSize zuojiankuohaophpcnzuojiankuohaophpcn 1) * sizeof(Bucket *), ht-youjiankuohaophpcnpersistent);
        if (t) {
            ht-youjiankuohaophpcnarBuckets = t;
            ht-youjiankuohaophpcnnTableSize = (ht-youjiankuohaophpcnnTableSize zuojiankuohaophpcnzuojiankuohaophpcn 1);
            ht-youjiankuohaophpcnnTableMask = ht-youjiankuohaophpcnnTableSize - 1;
            zend_hash_rehash(ht);
            return SUCCESS;
        }
        return FAILURE;
    }
    return SUCCESS;} 
 
 
 #define UPDATE_DATA(ht, p, pData, nDataSize) \
if (nDataSize == sizeof(void*)) { \
if ((p)-youjiankuohaophpcnpData != &(p)-youjiankuohaophpcnpDataPtr) { \
pefree_rel((p)-youjiankuohaophpcnpData, (ht)-youjiankuohaophpcnpersistent); \
} \
memcpy(&(p)-youjiankuohaophpcnpDataPtr, pData, sizeof(void *)); \
(p)-youjiankuohaophpcnpData = &(p)-youjiankuohaophpcnpDataPtr; \
} else { \
    if ((p)-youjiankuohaophpcnpData == &(p)-youjiankuohaophpcnpDataPtr) { \
        (p)-youjiankuohaophpcnpData = (void *) pemalloc_rel(nDataSize, (ht)-youjiankuohaophpcnpersistent); \
        (p)-youjiankuohaophpcnpDataPtr=NULL; \
    } else { \
        (p)-youjiankuohaophpcnpData = (void *) perealloc_rel((p)-youjiankuohaophpcnpData, nDataSize, (ht)-youjiankuohaophpcnpersistent);\
/* (p)-youjiankuohaophpcnpDataPtr is already NULL so no need to initialize it */ \
    } \
    memcpy((p)-youjiankuohaophpcnpData, pData, nDataSize); \
} #define INIT_DATA(ht, p, pData, nDataSize); \
if (nDataSize == sizeof(void*)) { \
memcpy(&(p)-youjiankuohaophpcnpDataPtr, pData, sizeof(void *)); \
(p)-youjiankuohaophpcnpData = &(p)-youjiankuohaophpcnpDataPtr; \
} else { \
    (p)-youjiankuohaophpcnpData = (void *) pemalloc_rel(nDataSize, (ht)-youjiankuohaophpcnpersistent);\
    if (!(p)-youjiankuohaophpcnpData) { \
        pefree_rel(p, (ht)-youjiankuohaophpcnpersistent); \
        return FAILURE; \
    } \
    memcpy((p)-youjiankuohaophpcnpData, pData, nDataSize); \
    (p)-youjiankuohaophpcnpDataPtr=NULL; \
} 
 
 static inline ulong zend_inline_hash_func(char *arKey, uint nKeyLength) {
    register ulong hash = 5381; /* variant with the hash unrolled eight times */
    for (; nKeyLength youjiankuohaophpcn= 8; nKeyLength -= 8) {
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
        hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++;
    }
    switch (nKeyLength) {
        case 7: hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++; /* fallthrough... */
        case 6: hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++; /* fallthrough... */
        case 5: hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++; /* fallthrough... */
        case 4: hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++; /* fallthrough... */
        case 3: hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++; /* fallthrough... */
        case 2: hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++; /* fallthrough... */
        case 1: hash = ((hash zuojiankuohaophpcnzuojiankuohaophpcn 5) + hash) + *arKey++; break;
        case 0: break;
    }
    return hash;}ulong zend_hash_func(char *arKey, uint nKeyLength) {
    return zend_inline_hash_func(arKey, nKeyLength);} //省略了int zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor) {
    uint i = 3;
    Bucket **tmp;
    zend_bool persistent = 1; 
     if (nSize youjiankuohaophpcn= 0x80000000) {/* prevent overflow */
        ht-youjiankuohaophpcnnTableSize = 0x80000000;
    } else {
        while ((1U zuojiankuohaophpcnzuojiankuohaophpcn i) zuojiankuohaophpcn nSize) {
            i++;
        }
        ht-youjiankuohaophpcnnTableSize = 1 zuojiankuohaophpcnzuojiankuohaophpcn i;
    } 
    ht-youjiankuohaophpcnnTableMask = ht-youjiankuohaophpcnnTableSize - 1;
    ht-youjiankuohaophpcnpDestructor = pDestructor;
    ht-youjiankuohaophpcnarBuckets = NULL;
    ht-youjiankuohaophpcnpListHead = NULL;
    ht-youjiankuohaophpcnpListTail = NULL;
    ht-youjiankuohaophpcnnNumOfElements = 0;
    ht-youjiankuohaophpcnnNextFreeElement = 0;
    ht-youjiankuohaophpcnpInternalPointer = NULL;
    ht-youjiankuohaophpcnpersistent = persistent;
    ht-youjiankuohaophpcnnApplyCount = 0;
    ht-youjiankuohaophpcnbApplyProtection = 1; 
 
    tmp = (Bucket **) calloc(ht-youjiankuohaophpcnnTableSize, sizeof(Bucket *));
    if (!tmp) {
        return FAILURE;
    }
    ht-youjiankuohaophpcnarBuckets = tmp; 
     return SUCCESS;} int zend_hash_add_or_update(HashTable *ht, char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag) {
    ulong h;
    uint nIndex;
    Bucket *p; 
     if (nKeyLength zuojiankuohaophpcn= 0) {
        return FAILURE;
    } 
    h = zend_inline_hash_func(arKey, nKeyLength);
    nIndex = h & ht-youjiankuohaophpcnnTableMask; 
    p = ht-youjiankuohaophpcnarBuckets[nIndex];     while (p != NULL) {
        if ((p-youjiankuohaophpcnh == h) && (p-youjiankuohaophpcnnKeyLength == nKeyLength)) {
            if (!memcmp(p-youjiankuohaophpcnarKey, arKey, nKeyLength)) {
                if (flag & HASH_ADD) {
                    return FAILURE;
                }                 if (ht-youjiankuohaophpcnpDestructor) {
                    ht-youjiankuohaophpcnpDestructor(p-youjiankuohaophpcnpData);
                }
                UPDATE_DATA(ht, p, pData, nDataSize);
                if (pDest) {
                *pDest = p-youjiankuohaophpcnpData;
            }
            return SUCCESS;
        }
    }
    p = p-youjiankuohaophpcnpNext;} 
p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht-youjiankuohaophpcnpersistent);if (!p) {
    return FAILURE;}memcpy(p-youjiankuohaophpcnarKey, arKey, nKeyLength);p-youjiankuohaophpcnnKeyLength = nKeyLength;INIT_DATA(ht, p, pData, nDataSize);p-youjiankuohaophpcnh = h;CONNECT_TO_BUCKET_DLLIST(p, ht-youjiankuohaophpcnarBuckets[nIndex]);if (pDest) {*pDest = p-youjiankuohaophpcnpData;} 
 
CONNECT_TO_GLOBAL_DLLIST(p, ht);ht-youjiankuohaophpcnarBuckets[nIndex] = p; 
 
ht-youjiankuohaophpcnnNumOfElements++;ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */return SUCCESS;} void zend_hash_destroy(HashTable *ht) {
    Bucket *p, *q; 
 
    p = ht-youjiankuohaophpcnpListHead;
    while (p != NULL) {
        q = p;
        p = p-youjiankuohaophpcnpListNext;
        if (ht-youjiankuohaophpcnpDestructor) {
            ht-youjiankuohaophpcnpDestructor(q-youjiankuohaophpcnpData);
        }
        if (q-youjiankuohaophpcnpData != &q-youjiankuohaophpcnpDataPtr) {
            pefree(q-youjiankuohaophpcnpData, ht-youjiankuohaophpcnpersistent);
        }
        pefree(q, ht-youjiankuohaophpcnpersistent);
    }
    pefree(ht-youjiankuohaophpcnarBuckets, ht-youjiankuohaophpcnpersistent); } 
 
 int zend_hash_find(HashTable *ht, char *arKey, uint nKeyLength, void **pData) {
    ulong h;
    uint nIndex;
    Bucket *p; 
 
    h = zend_inline_hash_func(arKey, nKeyLength);
    nIndex = h & ht-youjiankuohaophpcnnTableMask; 
    p = ht-youjiankuohaophpcnarBuckets[nIndex];
    while (p != NULL) {
        if ((p-youjiankuohaophpcnh == h) && (p-youjiankuohaophpcnnKeyLength == nKeyLength)) {
            if (!memcmp(p-youjiankuohaophpcnarKey, arKey, nKeyLength)) {
            *pData = p-youjiankuohaophpcnpData;
            return SUCCESS;
        }
    }
    p = p-youjiankuohaophpcnpNext;}return FAILURE;} 
 void zend_hash_display(HashTable *ht) {
    Bucket *p;
    uint i;
    int flag  = 0 ;     for (i = 0; i zuojiankuohaophpcn ht-youjiankuohaophpcnnTableSize; i++) {
        p = ht-youjiankuohaophpcnarBuckets[i];
        flag = 0;
        while (p != NULL) {
            printf("(%d %s zuojiankuohaophpcn==youjiankuohaophpcn 0x%lX %d)   ", i, p-youjiankuohaophpcnarKey, p-youjiankuohaophpcnh, p-youjiankuohaophpcnpNext);
            p = p-youjiankuohaophpcnpNext;
            flag = 1;
        }
        if (flag == 1) {
            printf("\n");
        }     } 
    p = ht-youjiankuohaophpcnpListTail;
    while (p != NULL) {
        printf("%s zuojiankuohaophpcn==youjiankuohaophpcn 0x%lX\n", p-youjiankuohaophpcnarKey, p-youjiankuohaophpcnh);
        p = p-youjiankuohaophpcnpListLast;
    }}int main() {
    int i;
    char ch[20];
    HashTable ht;
    zend_hash_init(&ht, 50, NULL, NULL);
    for (i = 30; i zuojiankuohaophpcn 68; i++) {
        sprintf(ch, "%d", i);
        ch[strlen(ch) + 1] = '\0';
        zend_hash_add_or_update(&ht, ch, strlen(ch) + 1, NULL, 0, NULL, 0);
    } 
    zend_hash_display(&ht);
    zend_hash_destroy(&ht);
    return 0;}?youjiankuohaophpcn

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

ECTouch移动商城系统
ECTouch移动商城系统

ECTouch是上海商创网络科技有限公司推出的一套基于 PHP 和 MySQL 数据库构建的开源且易于使用的移动商城网店系统!应用于各种服务器平台的高效、快速和易于管理的网店解决方案,采用稳定的MVC框架开发,完美对接ecshop系统与模板堂众多模板,为中小企业提供最佳的移动电商解决方案。ECTouch程序源代码完全无加密。安装时只需将已集成的文件夹放进指定位置,通过浏览器访问一键安装,无需对已有

下载

关于PHP源代码中Zend HashTable的解析

立即学习PHP免费学习笔记(深入)”;

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

2

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

2

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

0

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

0

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.29

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

25

2026.01.29

clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址
clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址

clawdbot龙虾机器人官网入口:https://clawd.bot/,clawdbot ai是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

16

2026.01.29

Golang 网络安全与加密实战
Golang 网络安全与加密实战

本专题系统讲解 Golang 在网络安全与加密技术中的应用,包括对称加密与非对称加密(AES、RSA)、哈希与数字签名、JWT身份认证、SSL/TLS 安全通信、常见网络攻击防范(如SQL注入、XSS、CSRF)及其防护措施。通过实战案例,帮助学习者掌握 如何使用 Go 语言保障网络通信的安全性,保护用户数据与隐私。

8

2026.01.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

622

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 3.1万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

php-src源码分析探索
php-src源码分析探索

共6课时 | 0.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号