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

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

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