php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
filter.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Wez Furlong <wez@thebrainroom.com> |
14 +----------------------------------------------------------------------+
15 */
16
17#include "php.h"
18#include "php_globals.h"
19#include "php_network.h"
21#include "ext/standard/file.h"
22#include <stddef.h>
23#include <fcntl.h>
24
25#include "php_streams_int.h"
26
27/* Global filter hash, copied to FG(stream_filters) on registration of volatile filter */
28static HashTable stream_filters_hash;
29
30/* Should only be used during core initialization */
32{
33 return &stream_filters_hash;
34}
35
36/* Normal hash selection/retrieval call */
38{
39 return (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
40}
41
42/* API for registering GLOBAL filters */
44{
45 int ret;
46 zend_string *str = zend_string_init_interned(filterpattern, strlen(filterpattern), 1);
47 ret = zend_hash_add_ptr(&stream_filters_hash, str, (void*)factory) ? SUCCESS : FAILURE;
49 return ret;
50}
51
52PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern)
53{
54 return zend_hash_str_del(&stream_filters_hash, filterpattern, strlen(filterpattern));
55}
56
57/* API for registering VOLATILE wrappers */
59{
60 if (!FG(stream_filters)) {
61 ALLOC_HASHTABLE(FG(stream_filters));
62 zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash) + 1, NULL, NULL, 0);
63 zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL);
64 }
65
66 return zend_hash_add_ptr(FG(stream_filters), filterpattern, (void*)factory) ? SUCCESS : FAILURE;
67}
68
69/* Buckets */
70
71PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent)
72{
73 int is_persistent = php_stream_is_persistent(stream);
74 php_stream_bucket *bucket;
75
76 bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
77 bucket->next = bucket->prev = NULL;
78
79 if (is_persistent && !buf_persistent) {
80 /* all data in a persistent bucket must also be persistent */
81 bucket->buf = pemalloc(buflen, 1);
82 memcpy(bucket->buf, buf, buflen);
83 bucket->buflen = buflen;
84 bucket->own_buf = 1;
85 } else {
86 bucket->buf = buf;
87 bucket->buflen = buflen;
88 bucket->own_buf = own_buf;
89 }
90 bucket->is_persistent = is_persistent;
91 bucket->refcount = 1;
92 bucket->brigade = NULL;
93
94 return bucket;
95}
96
97/* Given a bucket, returns a version of that bucket with a writeable buffer.
98 * If the original bucket has a refcount of 1 and owns its buffer, then it
99 * is returned unchanged.
100 * Otherwise, a copy of the buffer is made.
101 * In both cases, the original bucket is unlinked from its brigade.
102 * If a copy is made, the original bucket is delref'd.
103 * */
105{
107
109
110 if (bucket->refcount == 1 && bucket->own_buf) {
111 return bucket;
112 }
113
115 memcpy(retval, bucket, sizeof(*retval));
116
117 retval->buf = pemalloc(retval->buflen, retval->is_persistent);
118 memcpy(retval->buf, bucket->buf, retval->buflen);
119
120 retval->refcount = 1;
121 retval->own_buf = 1;
122
124
125 return retval;
126}
127
129{
132
133 (*left)->buf = pemalloc(length, in->is_persistent);
134 (*left)->buflen = length;
135 memcpy((*left)->buf, in->buf, length);
136 (*left)->refcount = 1;
137 (*left)->own_buf = 1;
138 (*left)->is_persistent = in->is_persistent;
139
140 (*right)->buflen = in->buflen - length;
141 (*right)->buf = pemalloc((*right)->buflen, in->is_persistent);
142 memcpy((*right)->buf, in->buf + length, (*right)->buflen);
143 (*right)->refcount = 1;
144 (*right)->own_buf = 1;
145 (*right)->is_persistent = in->is_persistent;
146
147 return SUCCESS;
148}
149
151{
152 if (--bucket->refcount == 0) {
153 if (bucket->own_buf) {
154 pefree(bucket->buf, bucket->is_persistent);
155 }
156 pefree(bucket, bucket->is_persistent);
157 }
158}
159
161{
162 bucket->next = brigade->head;
163 bucket->prev = NULL;
164
165 if (brigade->head) {
166 brigade->head->prev = bucket;
167 } else {
168 brigade->tail = bucket;
169 }
170 brigade->head = bucket;
171 bucket->brigade = brigade;
172}
173
175{
176 if (brigade->tail == bucket) {
177 return;
178 }
179
180 bucket->prev = brigade->tail;
181 bucket->next = NULL;
182
183 if (brigade->tail) {
184 brigade->tail->next = bucket;
185 } else {
186 brigade->head = bucket;
187 }
188 brigade->tail = bucket;
189 bucket->brigade = brigade;
190}
191
193{
194 if (bucket->prev) {
195 bucket->prev->next = bucket->next;
196 } else if (bucket->brigade) {
197 bucket->brigade->head = bucket->next;
198 }
199 if (bucket->next) {
200 bucket->next->prev = bucket->prev;
201 } else if (bucket->brigade) {
202 bucket->brigade->tail = bucket->prev;
203 }
204 bucket->brigade = NULL;
205 bucket->next = bucket->prev = NULL;
206}
207
208
209
210
211
212
213
214
215/* We allow very simple pattern matching for filter factories:
216 * if "convert.charset.utf-8/sjis" is requested, we search first for an exact
217 * match. If that fails, we try "convert.charset.*", then "convert.*"
218 * This means that we don't need to clog up the hashtable with a zillion
219 * charsets (for example) but still be able to provide them all as filters */
220PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
221{
222 HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
224 php_stream_filter *filter = NULL;
225 size_t n;
226 char *period;
227
228 n = strlen(filtername);
229
230 if (NULL != (factory = zend_hash_str_find_ptr(filter_hash, filtername, n))) {
231 filter = factory->create_filter(filtername, filterparams, persistent);
232 } else if ((period = strrchr(filtername, '.'))) {
233 /* try a wildcard */
234 char *wildname;
235
236 wildname = safe_emalloc(1, n, 3);
237 memcpy(wildname, filtername, n+1);
238 period = wildname + (period - filtername);
239 while (period && !filter) {
240 ZEND_ASSERT(period[0] == '.');
241 period[1] = '*';
242 period[2] = '\0';
243 if (NULL != (factory = zend_hash_str_find_ptr(filter_hash, wildname, strlen(wildname)))) {
244 filter = factory->create_filter(filtername, filterparams, persistent);
245 }
246
247 *period = '\0';
248 period = strrchr(wildname, '.');
249 }
250 efree(wildname);
251 }
252
253 if (filter == NULL) {
254 /* TODO: these need correct docrefs */
255 if (factory == NULL)
256 php_error_docref(NULL, E_WARNING, "Unable to locate filter \"%s\"", filtername);
257 else
258 php_error_docref(NULL, E_WARNING, "Unable to create or locate filter \"%s\"", filtername);
259 }
260
261 return filter;
262}
263
265{
266 php_stream_filter *filter;
267
269 memset(filter, 0, sizeof(php_stream_filter));
270
271 filter->fops = fops;
272 Z_PTR(filter->abstract) = abstract;
273 filter->is_persistent = persistent;
274
275 return filter;
276}
277
279{
280 if (filter->fops->dtor)
281 filter->fops->dtor(filter);
282 pefree(filter, filter->is_persistent);
283}
284
286{
287 filter->next = chain->head;
288 filter->prev = NULL;
289
290 if (chain->head) {
291 chain->head->prev = filter;
292 } else {
293 chain->tail = filter;
294 }
295 chain->head = filter;
296 filter->chain = chain;
297
298 return SUCCESS;
299}
300
305
307{
308 php_stream *stream = chain->stream;
309
310 filter->prev = chain->tail;
311 filter->next = NULL;
312 if (chain->tail) {
313 chain->tail->next = filter;
314 } else {
315 chain->head = filter;
316 }
317 chain->tail = filter;
318 filter->chain = chain;
319
320 if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) {
321 /* Let's going ahead and wind anything in the buffer through this filter */
322 php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
323 php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
325 php_stream_bucket *bucket;
326 size_t consumed = 0;
327
328 bucket = php_stream_bucket_new(stream, (char*) stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0);
329 php_stream_bucket_append(brig_inp, bucket);
330 status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL);
331
332 if (stream->readpos + consumed > (uint32_t)stream->writepos) {
333 /* No behaving filter should cause this. */
335 }
336
337 switch (status) {
338 case PSFS_ERR_FATAL:
339 while (brig_in.head) {
340 bucket = brig_in.head;
343 }
344 while (brig_out.head) {
345 bucket = brig_out.head;
348 }
349 php_error_docref(NULL, E_WARNING, "Filter failed to process pre-buffered data");
350 return FAILURE;
351 case PSFS_FEED_ME:
352 /* We don't actually need data yet,
353 leave this filter in a feed me state until data is needed.
354 Reset stream's internal read buffer since the filter is "holding" it. */
355 stream->readpos = 0;
356 stream->writepos = 0;
357 break;
358 case PSFS_PASS_ON:
359 /* If any data is consumed, we cannot rely upon the existing read buffer,
360 as the filtered data must replace the existing data, so invalidate the cache */
361 stream->writepos = 0;
362 stream->readpos = 0;
363
364 while (brig_outp->head) {
365 bucket = brig_outp->head;
366 /* Grow buffer to hold this bucket if need be.
367 TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */
368 if (stream->readbuflen - stream->writepos < bucket->buflen) {
369 stream->readbuflen += bucket->buflen;
370 stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent);
371 }
372 memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
373 stream->writepos += bucket->buflen;
374
377 }
378 break;
379 }
380 }
381
382 return SUCCESS;
383}
384
386{
387 if (php_stream_filter_append_ex(chain, filter) != SUCCESS) {
388 if (chain->head == filter) {
389 chain->head = NULL;
390 chain->tail = NULL;
391 } else {
392 filter->prev->next = NULL;
393 chain->tail = filter->prev;
394 }
395 }
396}
397
399{
400 php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
401 php_stream_bucket *bucket;
404 php_stream *stream;
405 size_t flushed_size = 0;
407
408 if (!filter->chain || !filter->chain->stream) {
409 /* Filter is not attached to a chain, or chain is somehow not part of a stream */
410 return FAILURE;
411 }
412
413 chain = filter->chain;
414 stream = chain->stream;
415
416 for(current = filter; current; current = current->next) {
418
419 status = current->fops->filter(stream, current, inp, outp, NULL, flags);
420 if (status == PSFS_FEED_ME) {
421 /* We've flushed the data far enough */
422 return SUCCESS;
423 }
424 if (status == PSFS_ERR_FATAL) {
425 return FAILURE;
426 }
427 /* Otherwise we have data available to PASS_ON
428 Swap the brigades and continue */
429 brig_temp = inp;
430 inp = outp;
431 outp = brig_temp;
432 outp->head = NULL;
433 outp->tail = NULL;
434
436 }
437
438 /* Last filter returned data via PSFS_PASS_ON
439 Do something with it */
440
441 for(bucket = inp->head; bucket; bucket = bucket->next) {
442 flushed_size += bucket->buflen;
443 }
444
445 if (flushed_size == 0) {
446 /* Unlikely, but possible */
447 return SUCCESS;
448 }
449
450 if (chain == &(stream->readfilters)) {
451 /* Dump any newly flushed data to the read buffer */
452 if (stream->readpos > 0) {
453 /* Back the buffer up */
454 memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos);
455 stream->readpos = 0;
456 stream->writepos -= stream->readpos;
457 }
458 if (flushed_size > (stream->readbuflen - stream->writepos)) {
459 /* Grow the buffer */
460 stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent);
461 }
462 while ((bucket = inp->head)) {
463 memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
464 stream->writepos += bucket->buflen;
467 }
468 } else if (chain == &(stream->writefilters)) {
469 /* Send flushed data to the stream */
470 while ((bucket = inp->head)) {
471 ssize_t count = stream->ops->write(stream, bucket->buf, bucket->buflen);
472 if (count > 0) {
473 stream->position += count;
474 }
477 }
478 }
479
480 return SUCCESS;
481}
482
484{
485 if (filter->prev) {
486 filter->prev->next = filter->next;
487 } else {
488 filter->chain->head = filter->next;
489 }
490 if (filter->next) {
491 filter->next->prev = filter->prev;
492 } else {
493 filter->chain->tail = filter->prev;
494 }
495
496 if (filter->res) {
497 zend_list_delete(filter->res);
498 }
499
500 if (call_dtor) {
502 return NULL;
503 }
504 return filter;
505}
strrchr(string $haystack, string $needle, bool $before_needle=false)
count(Countable|array $value, int $mode=COUNT_NORMAL)
DNS_STATUS status
Definition dns_win32.c:49
zend_long n
Definition ffi.c:4979
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
ffi persistent
Definition ffi.c:3633
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
const php_stream_filter_factory * factory
Definition filters.c:1900
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
PHPAPI php_stream_bucket * php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent)
Definition filter.c:71
PHPAPI php_stream_bucket * php_stream_bucket_make_writeable(php_stream_bucket *bucket)
Definition filter.c:104
PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket)
Definition filter.c:174
PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket)
Definition filter.c:150
PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter)
Definition filter.c:385
PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket)
Definition filter.c:192
PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket)
Definition filter.c:160
PHPAPI int php_stream_filter_register_factory(const char *filterpattern, const php_stream_filter_factory *factory)
Definition filter.c:43
PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter)
Definition filter.c:301
PHPAPI HashTable * php_get_stream_filters_hash_global(void)
Definition filter.c:31
PHPAPI int php_stream_filter_register_factory_volatile(zend_string *filterpattern, const php_stream_filter_factory *factory)
Definition filter.c:58
PHPAPI php_stream_filter * php_stream_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
Definition filter.c:220
PHPAPI HashTable * _php_get_stream_filters_hash(void)
Definition filter.c:37
PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length)
Definition filter.c:128
PHPAPI void php_stream_filter_free(php_stream_filter *filter)
Definition filter.c:278
PHPAPI php_stream_filter * _php_stream_filter_alloc(const php_stream_filter_ops *fops, void *abstract, uint8_t persistent STREAMS_DC)
Definition filter.c:264
PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern)
Definition filter.c:52
PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter)
Definition filter.c:285
PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish)
Definition filter.c:398
PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter)
Definition filter.c:306
PHPAPI php_stream_filter * php_stream_filter_remove(php_stream_filter *filter, int call_dtor)
Definition filter.c:483
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
lu_byte right
Definition minilua.c:4267
lu_byte left
Definition minilua.c:4266
#define PHPAPI
Definition php.h:71
#define PSFS_FLAG_FLUSH_CLOSE
#define PSFS_FLAG_FLUSH_INC
struct _php_stream_filter_chain php_stream_filter_chain
struct _php_stream_filter_ops php_stream_filter_ops
php_stream_filter_status_t
@ PSFS_ERR_FATAL
struct _php_stream_filter_factory php_stream_filter_factory
struct _php_stream_bucket php_stream_bucket
#define PSFS_FLAG_NORMAL
struct _php_stream_bucket_brigade php_stream_bucket_brigade
struct _php_stream php_stream
Definition php_streams.h:96
struct _php_stream_filter php_stream_filter
Definition php_streams.h:99
#define STREAMS_DC
Definition php_streams.h:53
#define php_stream_is_persistent(stream)
#define pemalloc_rel_orig(size, persistent)
zval * current
Definition session.c:1024
#define FG(v)
Definition file.h:117
php_stream_bucket * next
php_stream_bucket * prev
php_stream_bucket_brigade * brigade
php_stream_filter_status_t(* filter)(php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags)
void(* dtor)(php_stream_filter *thisfilter)
const php_stream_filter_ops * fops
php_stream_filter_chain * chain
php_stream_filter * prev
php_stream_filter * next
ssize_t(* write)(php_stream *stream, const char *buf, size_t count)
uint16_t is_persistent
const php_stream_ops * ops
size_t readbuflen
zend_off_t readpos
php_stream_filter_chain writefilters
size_t chunk_size
php_stream_filter_chain readfilters
zend_off_t position
zend_off_t writepos
unsigned char * readbuf
#define perealloc(ptr, size, persistent)
Definition zend_alloc.h:201
#define efree(ptr)
Definition zend_alloc.h:155
#define pefree(ptr, persistent)
Definition zend_alloc.h:191
#define pemalloc(size, persistent)
Definition zend_alloc.h:189
#define safe_emalloc(nmemb, size, offset)
Definition zend_alloc.h:154
#define pecalloc(nmemb, size, persistent)
Definition zend_alloc.h:200
#define ALLOC_HASHTABLE(ht)
Definition zend_alloc.h:231
struct _zval_struct zval
strlen(string $string)
zend_string_release_ex(func->internal_function.function_name, 0)
#define E_WARNING
Definition zend_errors.h:24
ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor)
Definition zend_hash.c:2240
ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *str, size_t len)
Definition zend_hash.c:1661
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
ZEND_API zend_result ZEND_FASTCALL zend_list_delete(zend_resource *res)
Definition zend_list.c:46
struct _zend_string zend_string
#define ZEND_ASSERT(c)
ZEND_API zend_string_init_interned_func_t zend_string_init_interned
Definition zend_string.c:31
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_PTR(zval)
@ FAILURE
Definition zend_types.h:61
zval retval
zval * ret