-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathphp_mdparser.h
More file actions
133 lines (111 loc) · 5.3 KB
/
php_mdparser.h
File metadata and controls
133 lines (111 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
+----------------------------------------------------------------------+
| Copyright (c) 2025-2026, Ilia Alshanetsky |
| Copyright (c) 2025-2026, Advanced Internet Designs Inc. |
+----------------------------------------------------------------------+
| This source file is subject to the BSD 3-Clause license that is |
| bundled with this package in the file LICENSE. |
+----------------------------------------------------------------------+
| Author: Ilia Alshanetsky <ilia@ilia.ws> |
+----------------------------------------------------------------------+
*/
#ifndef PHP_MDPARSER_H
#define PHP_MDPARSER_H
#define PHP_MDPARSER_VERSION "0.3.0"
extern zend_module_entry mdparser_module_entry;
#define phpext_mdparser_ptr &mdparser_module_entry
#ifdef PHP_WIN32
#define PHP_MDPARSER_API __declspec(dllexport)
#else
#define PHP_MDPARSER_API
#endif
#include "php.h"
#include "cmark-gfm.h"
/* PHP 8.3 compat shim for zend_register_internal_class_with_flags
* (added in 8.4). gen_stub.php emits the 8.4+ variant when it sees
* `final readonly class` in the stub, but we still target 8.3.
* Providing a static inline fallback keeps the generated arginfo.h
* unchanged and lets 8.3 builds compile and link. */
#if PHP_VERSION_ID < 80400
static inline zend_class_entry *zend_register_internal_class_with_flags(
zend_class_entry *class_entry,
zend_class_entry *parent_ce,
uint32_t flags)
{
zend_class_entry *registered = zend_register_internal_class_ex(class_entry, parent_ce);
registered->ce_flags |= flags;
return registered;
}
#endif
extern zend_class_entry *mdparser_parser_ce;
extern zend_class_entry *mdparser_options_ce;
extern zend_class_entry *mdparser_exception_ce;
typedef struct _mdparser_parser_obj {
int cmark_options;
int extension_mask;
int postprocess_mask;
/* Cached cmark_parser, built lazily on first render. Reused across
* subsequent renders because cmark_parser_finish calls
* cmark_parser_reset internally before returning, leaving the
* parser in a clean state with the same extension list attached.
*
* cmark_parser_reset is static in vendor/cmark/src/blocks.c so we
* cannot call it from outside; we rely on _finish's internal
* reset for the happy path, and rebuild from scratch after any
* render that did not complete cleanly (parser_dirty flag).
*
* Isolation invariant: after every render the parser holds no
* state from prior input -- no link reference definitions, no
* inline subject leftovers, no buffered partial input. Verified
* via tests/033_parser_reuse_isolation.phpt. */
cmark_parser *cmark_parser;
bool parser_dirty;
zend_object std;
} mdparser_parser_obj;
static inline mdparser_parser_obj *mdparser_parser_from_obj(zend_object *obj) {
return (mdparser_parser_obj *)((char *)(obj) - offsetof(mdparser_parser_obj, std));
}
#define Z_MDPARSER_PARSER_P(zv) mdparser_parser_from_obj(Z_OBJ_P(zv))
/* Bits in extension_mask matching cmark-gfm core extension names. */
#define MDPARSER_EXT_TABLES (1 << 0)
#define MDPARSER_EXT_STRIKETHROUGH (1 << 1)
#define MDPARSER_EXT_TASKLIST (1 << 2)
#define MDPARSER_EXT_AUTOLINK (1 << 3)
#define MDPARSER_EXT_TAGFILTER (1 << 4)
#define MDPARSER_EXT_COUNT 5
/* Postprocess flags. These are not cmark options or extensions; they
* select string-level transforms applied to the rendered HTML after
* cmark is done. Currently only HTML output uses these. */
#define MDPARSER_PP_HEADING_ANCHORS (1 << 0)
#define MDPARSER_PP_NOFOLLOW_LINKS (1 << 1)
/* Hard cap on input size. 256 MB is far above any realistic document
* and well below cmark's internal bufsize_t (int32) overflow edge. */
#define MDPARSER_MAX_INPUT_SIZE ((size_t)(256UL * 1024UL * 1024UL))
/* Hard cap on AST walker recursion depth. cmark's own parser is
* iterative and happily produces trees thousands of levels deep from
* one-byte-per-level input like `>` × N. Walking such a tree with
* a recursive PHP-array builder would smash the C stack. */
#define MDPARSER_MAX_AST_DEPTH 1000
/* Extension pointers resolved once at MINIT so per-parse attachment
* is a bitmask loop instead of 5 registry walks with strcmp. */
typedef struct {
int bit;
cmark_syntax_extension *ptr;
} mdparser_cached_extension;
extern mdparser_cached_extension mdparser_cached_extensions[MDPARSER_EXT_COUNT];
/* Process-wide cmark_mem backed by Zend MM (ecalloc/erealloc/efree).
* Passed to every cmark_*_with_mem call so cmark allocations respect
* memory_limit and OOM goes through Zend MM bailout instead of abort. */
extern cmark_mem mdparser_zend_mem;
/* Default masks, computed once from mdparser_options_fields at MINIT. */
extern int mdparser_default_cmark_options;
extern int mdparser_default_extension_mask;
extern int mdparser_default_postprocess_mask;
/* Registration entry points (defined in the respective .c files) */
void mdparser_parser_register_class(void);
void mdparser_options_register_class(void);
void mdparser_exception_register_class(void);
/* Default-options helpers (defined in mdparser_options.c) */
void mdparser_options_init_defaults(void);
void mdparser_options_read_masks(zval *options_zv, int *cmark_options, int *extension_mask, int *postprocess_mask);
#endif /* PHP_MDPARSER_H */