PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/ext/stream/ext_stream-user-filters.cpp

https://gitlab.com/alvinahmadov2/hhvm
C++ | 406 lines | 308 code | 56 blank | 42 comment | 36 complexity | 60ac5a5c43a927c630ffe1dd60ae87ca MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2015 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. +----------------------------------------------------------------------+
  16. */
  17. #include "hphp/runtime/ext/stream/ext_stream-user-filters.h"
  18. #include "hphp/runtime/ext/stream/ext_stream.h"
  19. #include "hphp/runtime/base/array-init.h"
  20. #include "hphp/runtime/ext/extension.h"
  21. #include "hphp/runtime/base/builtin-functions.h"
  22. #include "hphp/runtime/base/file.h"
  23. #include "hphp/runtime/ext/array/ext_array.h"
  24. #include "hphp/runtime/ext/std/ext_std.h"
  25. #include "hphp/system/systemlib.h"
  26. namespace HPHP {
  27. ///////////////////////////////////////////////////////////////////////////////
  28. const StaticString s_filter("filter");
  29. const StaticString s_onCreate("onCreate");
  30. const StaticString s_onClose("onClose");
  31. const StaticString s_bucket_class("__SystemLib\\StreamFilterBucket");
  32. ///////////////////////////////////////////////////////////////////////////////
  33. const StaticString s_default_filters_register_func(
  34. "__SystemLib\\register_default_stream_filters");
  35. ///////////////////////////////////////////////////////////////////////////////
  36. struct StreamFilterRepository {
  37. void add(const String& key, const Variant& v) {
  38. m_filters.add(key, v);
  39. }
  40. void detach() {
  41. m_filters.detach();
  42. }
  43. bool exists(const String& needle) const {
  44. if (m_filters.exists(needle.toKey())) {
  45. return true;
  46. }
  47. /* Could not find exact match, now try wildcard match */
  48. int lastDotPos = needle.rfind('.');
  49. if (String::npos == lastDotPos) {
  50. return false;
  51. }
  52. String wildcard = needle.substr(0, lastDotPos) + ".*";
  53. return m_filters.exists(wildcard.toKey());
  54. }
  55. Variant rvalAt(const String& needle) const {
  56. /* First try to find exact match, afterwards try wildcard matches */
  57. int lastDotPos = needle.rfind('.');
  58. if (String::npos == lastDotPos || m_filters.exists(needle.toKey())) {
  59. return m_filters.rvalAtRef(needle);
  60. }
  61. String wildcard = needle.substr(0, lastDotPos) + ".*";
  62. return m_filters.rvalAtRef(wildcard);
  63. }
  64. const Array& filtersAsArray() const {
  65. return m_filters;
  66. }
  67. bool isNull() const {
  68. return m_filters.isNull();
  69. }
  70. template<class F> void scan(F& mark) const {
  71. mark(m_filters);
  72. }
  73. private:
  74. Array m_filters;
  75. };
  76. ///////////////////////////////////////////////////////////////////////////////
  77. struct StreamUserFilters final : RequestEventHandler {
  78. virtual ~StreamUserFilters() {}
  79. bool registerFilter(const String& name, const String& class_name) {
  80. if (name.empty()) {
  81. raise_warning("stream_filter_register(): Filter name cannot be empty");
  82. return false;
  83. }
  84. if (class_name.empty()) {
  85. raise_warning("stream_filter_register(): Class name cannot be empty");
  86. return false;
  87. }
  88. if (m_registeredFilters.exists(name)) {
  89. return false;
  90. }
  91. m_registeredFilters.add(name, class_name);
  92. return true;
  93. }
  94. Variant prepend(const Resource& stream,
  95. const String& filtername,
  96. const Variant& readwrite,
  97. const Variant& params) {
  98. return appendOrPrependFilter(stream,
  99. filtername,
  100. readwrite,
  101. params,
  102. /* append = */ false);
  103. }
  104. Variant append(const Resource& stream,
  105. const String& filtername,
  106. const Variant& readwrite,
  107. const Variant& params) {
  108. return appendOrPrependFilter(stream,
  109. filtername,
  110. readwrite,
  111. params,
  112. /* append = */ true);
  113. }
  114. void requestInit() override {
  115. vm_call_user_func(s_default_filters_register_func, empty_array_ref);
  116. }
  117. void requestShutdown() override {
  118. m_registeredFilters.detach();
  119. }
  120. private:
  121. Variant appendOrPrependFilter(const Resource& stream,
  122. const String& filtername,
  123. const Variant& readwrite,
  124. const Variant& params,
  125. bool append) {
  126. const char* func_name =
  127. append ? "stream_filter_append()" : "stream_filter_prepend()";
  128. if (!m_registeredFilters.exists(filtername)) {
  129. raise_warning("%s: unable to locate filter \"%s\"",
  130. func_name,
  131. filtername.data());
  132. return false;
  133. }
  134. auto file = cast<File>(stream);
  135. int mode = readwrite.toInt32();
  136. if (!mode) {
  137. auto str = file->getMode();
  138. /* The documentation says a read filter is only created for 'r' and '+'
  139. * modes, but the implementation will always create one unless
  140. * STREAM_FILTER_WRITE is passed.
  141. *
  142. * This branch is only executed if no STREAM_FILTER* args were passed,
  143. * so we always create a READ filter.
  144. */
  145. mode = k_STREAM_FILTER_READ;
  146. if (str.find('+') != -1 || str.find('w') != -1 || str.find('a') != -1) {
  147. mode |= k_STREAM_FILTER_WRITE;
  148. }
  149. }
  150. if (!(mode & k_STREAM_FILTER_ALL)) {
  151. return false;
  152. }
  153. // If it's ALL we create two resources, but only return one - this
  154. // matches Zend, and is the documented behavior.
  155. req::ptr<StreamFilter> ret;
  156. if (mode & k_STREAM_FILTER_READ) {
  157. auto resource = createInstance(func_name,
  158. file,
  159. filtername,
  160. params);
  161. if (!resource) {
  162. return false;
  163. }
  164. ret = resource;
  165. if (append) {
  166. file->appendReadFilter(resource);
  167. } else {
  168. file->prependReadFilter(resource);
  169. }
  170. }
  171. if (mode & k_STREAM_FILTER_WRITE) {
  172. auto resource = createInstance(func_name,
  173. file,
  174. filtername,
  175. params);
  176. if (!resource) {
  177. return false;
  178. }
  179. ret = resource;
  180. if (append) {
  181. file->appendWriteFilter(resource);
  182. } else {
  183. file->prependWriteFilter(resource);
  184. }
  185. }
  186. return Variant(std::move(ret));
  187. }
  188. req::ptr<StreamFilter> createInstance(const char* php_func,
  189. req::ptr<File> stream,
  190. const String& filter,
  191. const Variant& params) {
  192. auto class_name = m_registeredFilters.rvalAt(filter).asCStrRef();
  193. Class* class_ = Unit::getClass(class_name.get(), true);
  194. Object obj = Object();
  195. if (LIKELY(class_ != nullptr)) {
  196. PackedArrayInit ctor_args(3);
  197. ctor_args.append(Variant(stream));
  198. ctor_args.append(filter);
  199. ctor_args.append(params);
  200. obj = Object::attach(
  201. g_context->createObject(class_name.get(), ctor_args.toArray())
  202. );
  203. auto created = obj->o_invoke(s_onCreate, Array::Create());
  204. /* - true: documented value for success
  205. * - null: undocumented default successful value
  206. * - false: documented value for failure
  207. */
  208. if (!(created.isNull() || created.toBoolean())) {
  209. obj.reset();
  210. }
  211. } else {
  212. raise_warning("%s: user-filter \"%s\" requires class \"%s\", but that "
  213. "class " "is not defined",
  214. php_func,
  215. filter.data(),
  216. class_name.data());
  217. // Fall through, as to match Zend, the warning below should also be raised
  218. }
  219. if (obj.isNull()) {
  220. raise_warning("%s: unable to create or locate filter \"%s\"",
  221. php_func,
  222. filter.data());
  223. return nullptr;
  224. }
  225. return req::make<StreamFilter>(obj, stream);
  226. }
  227. public:
  228. void vscan(IMarker& mark) const override {
  229. m_registeredFilters.scan(mark);
  230. }
  231. public:
  232. StreamFilterRepository m_registeredFilters;
  233. };
  234. IMPLEMENT_STATIC_REQUEST_LOCAL(StreamUserFilters, s_stream_user_filters);
  235. ///////////////////////////////////////////////////////////////////////////////
  236. // StreamFilter
  237. int64_t StreamFilter::invokeFilter(const req::ptr<BucketBrigade>& in,
  238. const req::ptr<BucketBrigade>& out,
  239. bool closing) {
  240. auto consumedTV = make_tv<KindOfInt64>(0);
  241. auto consumedRef = Variant::attach(RefData::Make(consumedTV));
  242. PackedArrayInit params(4);
  243. params.append(Variant(in));
  244. params.append(Variant(out));
  245. params.append(consumedRef);
  246. params.append(closing);
  247. return m_filter->o_invoke(s_filter, params.toArray()).toInt64();
  248. }
  249. void StreamFilter::invokeOnClose() {
  250. m_filter->o_invoke(s_onClose, Array::Create());
  251. }
  252. bool StreamFilter::remove() {
  253. if (!m_stream) {
  254. return false;
  255. }
  256. auto ret = m_stream->removeFilter(req::ptr<StreamFilter>(this));
  257. m_stream.reset();
  258. return ret;
  259. }
  260. ///////////////////////////////////////////////////////////////////////////////
  261. // BucketBrigade
  262. BucketBrigade::BucketBrigade(const String& data) {
  263. PackedArrayInit ai(2);
  264. ai.append(data);
  265. ai.append(data.length());
  266. auto bucket = g_context->createObject(s_bucket_class.get(), ai.toArray());
  267. appendBucket(Object::attach(bucket));
  268. }
  269. void BucketBrigade::appendBucket(const Object& bucket) {
  270. m_buckets.push_back(bucket);
  271. }
  272. void BucketBrigade::prependBucket(const Object& bucket) {
  273. m_buckets.push_front(bucket);
  274. }
  275. Object BucketBrigade::popFront() {
  276. if (m_buckets.empty()) {
  277. return Object();
  278. }
  279. auto bucket = m_buckets.front();
  280. m_buckets.pop_front();
  281. return bucket;
  282. }
  283. String BucketBrigade::createString() {
  284. StringBuffer buffer;
  285. for (auto& bucket_obj: m_buckets) {
  286. buffer.append(bucket_obj.toString());
  287. }
  288. return buffer.detach();
  289. }
  290. ///////////////////////////////////////////////////////////////////////////////
  291. bool HHVM_FUNCTION(stream_filter_register,
  292. const String& name,
  293. const String& classname) {
  294. return s_stream_user_filters.get()->registerFilter(name, classname);
  295. }
  296. Array HHVM_FUNCTION(stream_get_filters) {
  297. auto filters = s_stream_user_filters.get()->m_registeredFilters;
  298. if (UNLIKELY(filters.isNull())) {
  299. return empty_array();
  300. }
  301. return array_keys_helper(filters.filtersAsArray()).toArray();
  302. }
  303. Variant HHVM_FUNCTION(stream_filter_append,
  304. const Resource& stream,
  305. const String& filtername,
  306. const Variant& readwrite,
  307. const Variant& params) {
  308. return s_stream_user_filters.get()->append(stream,
  309. filtername,
  310. readwrite,
  311. params);
  312. }
  313. Variant HHVM_FUNCTION(stream_filter_prepend,
  314. const Resource& stream,
  315. const String& filtername,
  316. const Variant& readwrite,
  317. const Variant& params) {
  318. return s_stream_user_filters.get()->prepend(stream,
  319. filtername,
  320. readwrite,
  321. params);
  322. }
  323. bool HHVM_FUNCTION(stream_filter_remove, const Resource& resource) {
  324. return cast<StreamFilter>(resource)->remove();
  325. }
  326. Variant HHVM_FUNCTION(stream_bucket_make_writeable, const Resource& bb_res) {
  327. return cast<BucketBrigade>(bb_res)->popFront();
  328. }
  329. void HHVM_FUNCTION(stream_bucket_append, const Resource& bb_res, const Object& bucket) {
  330. cast<BucketBrigade>(bb_res)->appendBucket(bucket);
  331. }
  332. void HHVM_FUNCTION(stream_bucket_prepend, const Resource& bb_res, const Object& bucket) {
  333. cast<BucketBrigade>(bb_res)->prependBucket(bucket);
  334. }
  335. void StandardExtension::initStreamUserFilters() {
  336. HHVM_RC_INT(STREAM_FILTER_READ, k_STREAM_FILTER_READ);
  337. HHVM_RC_INT(STREAM_FILTER_WRITE, k_STREAM_FILTER_WRITE);
  338. HHVM_RC_INT(STREAM_FILTER_ALL, k_STREAM_FILTER_ALL);
  339. HHVM_FE(stream_get_filters);
  340. HHVM_FE(stream_filter_register);
  341. HHVM_FE(stream_filter_append);
  342. HHVM_FE(stream_filter_prepend);
  343. HHVM_FE(stream_filter_remove);
  344. HHVM_FE(stream_bucket_make_writeable);
  345. HHVM_FE(stream_bucket_append);
  346. HHVM_FE(stream_bucket_prepend);
  347. loadSystemlib("stream-user-filters");
  348. }
  349. ///////////////////////////////////////////////////////////////////////////////
  350. }