2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/stream/ext_stream-user-filters.h"
19 #include "hphp/runtime/base/base-includes.h"
20 #include "hphp/runtime/ext/ext_array.h"
21 #include "hphp/system/systemlib.h"
25 ///////////////////////////////////////////////////////////////////////////////
27 const StaticString
s_filter("filter");
28 const StaticString
s_onCreate("onCreate");
29 const StaticString
s_onClose("onClose");
31 const StaticString
s_bucket_class("__SystemLib\\StreamFilterBucket");
33 ///////////////////////////////////////////////////////////////////////////////
35 const StaticString
s_default_filters_register_func(
36 "__SystemLib\\register_default_stream_filters");
38 class StreamUserFilters
: public RequestEventHandler
{
40 virtual ~StreamUserFilters() {}
41 Array m_registeredFilters
;
43 bool registerFilter(const String
& name
, const String
& class_name
) {
44 if (m_registeredFilters
.exists(name
)) {
47 m_registeredFilters
.add(name
, class_name
);
51 Variant
prepend(CResRef stream
,
52 const String
& filtername
,
55 return appendOrPrependFilter(stream
,
59 /* append = */ false);
62 Variant
append(CResRef stream
,
63 const String
& filtername
,
66 return appendOrPrependFilter(stream
,
73 virtual void requestInit() {
74 vm_call_user_func(s_default_filters_register_func
, empty_array
);
77 virtual void requestShutdown() {}
79 Variant
appendOrPrependFilter(CResRef stream
,
80 const String
& filtername
,
84 const char* func_name
=
85 append
? "stream_filter_append()" : "stream_filter_prepend()";
87 if (!m_registeredFilters
.exists(filtername
)) {
88 raise_warning("%s: unable to locate filter \"%s\"",
94 auto file
= stream
.getTyped
<File
>();
97 int mode
= readwrite
.toInt32();
99 auto str
= file
->getMode();
100 /* The documentation says a read filter is only created for 'r' and '+'
101 * modes, but the implementation will always create one unless
102 * STREAM_FILTER_WRITE is passed.
104 * This branch is only executed if no STREAM_FILTER* args were passed,
105 * so we always create a READ filter.
107 mode
= k_STREAM_FILTER_READ
;
108 if (str
.find('+') != -1 || str
.find('w') != -1 || str
.find('a') != -1) {
109 mode
|= k_STREAM_FILTER_WRITE
;
112 if (!(mode
& k_STREAM_FILTER_ALL
)) {
116 // If it's ALL we create two resources, but only return one - this
117 // matches Zend, and is the documented behavior.
119 if (mode
& k_STREAM_FILTER_READ
) {
120 auto resource
= createInstance(func_name
,
124 if (resource
.isNull()) {
129 file
->appendReadFilter(resource
);
131 file
->prependReadFilter(resource
);
134 if (mode
& k_STREAM_FILTER_WRITE
) {
135 auto resource
= createInstance(func_name
,
139 if (resource
.isNull()) {
144 file
->appendWriteFilter(resource
);
146 file
->prependWriteFilter(resource
);
152 Resource
createInstance(const char* php_func
,
153 const Resource
& stream
,
154 const String
& filter
,
156 auto class_name
= m_registeredFilters
.rvalAt(filter
).asCStrRef();
157 Class
* class_
= Unit::lookupClass(class_name
.get());
158 Object obj
= null_object
;
160 if (LIKELY(class_
!= nullptr)) {
161 ArrayInit
ctor_args(3);
162 ctor_args
.set(stream
);
163 ctor_args
.set(filter
);
164 ctor_args
.set(params
);
165 obj
= g_context
->createObject(class_name
.get(), ctor_args
.toArray());
166 auto created
= obj
->o_invoke(s_onCreate
, Array::Create());
167 /* - true: documented value for success
168 * - null: undocumented default successful value
169 * - false: documented value for failure
171 if (!(created
.isNull() || created
.toBoolean())) {
175 raise_warning("%s: user-filter \"%s\" requires class \"%s\", but that "
176 "class " "is not defined",
180 // Fall through, as to match Zend, the warning below should also be raised
184 raise_warning("%s: unable to create or locate filter \"%s\"",
187 return null_resource
;
190 return Resource(NEWOBJ(StreamFilter
)(obj
));
193 IMPLEMENT_STATIC_REQUEST_LOCAL(StreamUserFilters
, s_stream_user_filters
);
195 ///////////////////////////////////////////////////////////////////////////////
198 int64_t StreamFilter::invokeFilter(Resource in
,
201 auto consumedTV
= make_tv
<KindOfInt64
>(0);
202 auto consumedRef
= RefData::Make(consumedTV
);
207 params
.set(consumedRef
);
209 return m_filter
->o_invoke(s_filter
, params
.toArray()).toInt64();
212 void StreamFilter::invokeOnClose() {
213 m_filter
->o_invoke(s_onClose
, Array::Create());
216 ///////////////////////////////////////////////////////////////////////////////
219 BucketBrigade::BucketBrigade(const String
& data
) {
222 ai
.set(data
.length());
223 auto bucket
= g_context
->createObject(s_bucket_class
.get(), ai
.toArray());
224 appendBucket(bucket
);
227 void BucketBrigade::appendBucket(CObjRef bucket
) {
228 m_buckets
.push_back(bucket
);
231 void BucketBrigade::prependBucket(CObjRef bucket
) {
232 m_buckets
.push_front(bucket
);
235 Object
BucketBrigade::popFront() {
236 if (m_buckets
.empty()) {
239 auto bucket
= m_buckets
.front();
240 m_buckets
.pop_front();
244 String
BucketBrigade::createString() {
246 for (auto& bucket_obj
: m_buckets
) {
247 buffer
.append(bucket_obj
.toString());
249 return buffer
.detach();
252 ///////////////////////////////////////////////////////////////////////////////
254 bool HHVM_FUNCTION(stream_filter_register
,
256 const String
& classname
) {
257 return s_stream_user_filters
.get()->registerFilter(name
, classname
);
260 Array
HHVM_FUNCTION(stream_get_filters
) {
261 auto filters
= s_stream_user_filters
.get()->m_registeredFilters
;
262 if (UNLIKELY(filters
.isNull())) {
263 return Array::Create();
265 return f_array_keys(filters
).toArray();
268 Variant
HHVM_FUNCTION(stream_filter_append
,
270 const String
& filtername
,
273 return s_stream_user_filters
.get()->append(stream
,
279 Variant
HHVM_FUNCTION(stream_filter_prepend
,
281 const String
& filtername
,
284 return s_stream_user_filters
.get()->prepend(stream
,
290 Variant
HHVM_FUNCTION(stream_bucket_make_writeable
, CResRef bb_res
) {
291 auto brigade
= bb_res
.getTyped
<BucketBrigade
>();
293 auto ret
= brigade
->popFront();
297 void HHVM_FUNCTION(stream_bucket_append
, CResRef bb_res
, CObjRef bucket
) {
298 auto brigade
= bb_res
.getTyped
<BucketBrigade
>();
300 brigade
->appendBucket(bucket
);
303 void HHVM_FUNCTION(stream_bucket_prepend
, CResRef bb_res
, CObjRef bucket
) {
304 auto brigade
= bb_res
.getTyped
<BucketBrigade
>();
306 brigade
->prependBucket(bucket
);
309 ///////////////////////////////////////////////////////////////////////////////