PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/ext/spl/ext_spl.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 381 lines | 313 code | 44 blank | 24 comment | 44 complexity | 39e1293801fb932290a6724c348e3634 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 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/spl/ext_spl.h"
  18. #include "hphp/runtime/ext/std/ext_std_classobj.h"
  19. #include "hphp/runtime/ext/std/ext_std_math.h"
  20. #include "hphp/runtime/ext/std/ext_std_file.h"
  21. #include "hphp/runtime/ext/string/ext_string.h"
  22. #include "hphp/runtime/base/autoload-handler.h"
  23. #include "hphp/runtime/base/array-init.h"
  24. #include "hphp/runtime/base/comparisons.h"
  25. #include "hphp/runtime/base/directory.h"
  26. #include "hphp/runtime/base/glob-stream-wrapper.h"
  27. #include "hphp/runtime/base/request-event-handler.h"
  28. #include "hphp/runtime/base/stream-wrapper-registry.h"
  29. #include "hphp/runtime/base/string-util.h"
  30. #include "hphp/runtime/vm/vm-regs.h"
  31. #include "hphp/system/systemlib.h"
  32. #include "hphp/util/string-vsnprintf.h"
  33. namespace HPHP {
  34. ///////////////////////////////////////////////////////////////////////////////
  35. const StaticString
  36. s_spl_autoload("spl_autoload"),
  37. s_spl_autoload_call("spl_autoload_call"),
  38. s_rewind("rewind"),
  39. s_valid("valid"),
  40. s_next("next"),
  41. s_current("current"),
  42. s_key("key"),
  43. s_getIterator("getIterator"),
  44. s_directory_iterator("DirectoryIterator");
  45. void throw_spl_exception(const char *fmt, ...) ATTRIBUTE_PRINTF(1,2);
  46. void throw_spl_exception(const char *fmt, ...) {
  47. va_list ap;
  48. va_start(ap, fmt);
  49. std::string msg;
  50. string_vsnprintf(msg, fmt, ap);
  51. va_end(ap);
  52. SystemLib::throwExceptionObject(Variant(msg));
  53. }
  54. static bool s_inited = false;
  55. static int64_t s_hash_mask_handle = 0;
  56. static Mutex s_mutex;
  57. String HHVM_FUNCTION(spl_object_hash, const Object& obj) {
  58. if (!s_inited) {
  59. Lock lock(s_mutex);
  60. if (!s_inited) {
  61. HHVM_FN(mt_srand)();
  62. s_hash_mask_handle |= HHVM_FN(mt_rand)(); s_hash_mask_handle <<= 16;
  63. s_hash_mask_handle |= HHVM_FN(mt_rand)(); s_hash_mask_handle <<= 16;
  64. s_hash_mask_handle |= HHVM_FN(mt_rand)(); s_hash_mask_handle <<= 16;
  65. s_hash_mask_handle |= HHVM_FN(mt_rand)();
  66. s_inited = true;
  67. }
  68. }
  69. char buf[33];
  70. // Using the object address here would interfere with a moving GC algorithm.
  71. // See t6299529.
  72. snprintf(buf, sizeof(buf), "%032" PRIx64,
  73. s_hash_mask_handle ^ (int64_t)obj.get());
  74. return String(buf, CopyString);
  75. }
  76. // Using the object address here could interfere with a moving GC algorithm.
  77. // See t6299529.
  78. int64_t HHVM_FUNCTION(hphp_object_pointer, const Object& obj) {
  79. return (int64_t)obj.get();
  80. }
  81. Variant HHVM_FUNCTION(hphp_get_this) {
  82. return g_context->getThis();
  83. }
  84. Variant HHVM_FUNCTION(class_implements, const Variant& obj,
  85. bool autoload /* = true */) {
  86. Class* cls;
  87. if (obj.isString()) {
  88. cls = Unit::getClass(obj.getStringData(), autoload);
  89. if (!cls) {
  90. String err = "class_implements(): Class %s does not exist";
  91. if (autoload) {
  92. err += " and could not be loaded";
  93. }
  94. raise_warning(err.c_str(), obj.toString().c_str());
  95. return false;
  96. }
  97. } else if (obj.isObject()) {
  98. cls = obj.getObjectData()->getVMClass();
  99. } else {
  100. raise_warning("class_implements(): object or string expected");
  101. return false;
  102. }
  103. Array ret(Array::Create());
  104. const Class::InterfaceMap& ifaces = cls->allInterfaces();
  105. for (int i = 0, size = ifaces.size(); i < size; i++) {
  106. ret.set(ifaces[i]->nameStr(), VarNR(ifaces[i]->name()));
  107. }
  108. return ret;
  109. }
  110. Variant HHVM_FUNCTION(class_parents, const Variant& obj,
  111. bool autoload /* = true */) {
  112. Class* cls;
  113. if (obj.isString()) {
  114. cls = Unit::getClass(obj.getStringData(), autoload);
  115. if (!cls) {
  116. String err = "class_parents(): Class %s does not exist";
  117. if (autoload) {
  118. err += " and could not be loaded";
  119. }
  120. raise_warning(err.c_str(), obj.toString().c_str());
  121. return false;
  122. }
  123. } else if (obj.isObject()) {
  124. cls = obj.getObjectData()->getVMClass();
  125. } else {
  126. raise_warning("class_parents(): object or string expected");
  127. return false;
  128. }
  129. Array ret(Array::Create());
  130. for (cls = cls->parent(); cls; cls = cls->parent()) {
  131. ret.set(cls->nameStr(), VarNR(cls->name()));
  132. }
  133. return ret;
  134. }
  135. Variant HHVM_FUNCTION(class_uses, const Variant& obj,
  136. bool autoload /* = true */) {
  137. Class* cls;
  138. if (obj.isString()) {
  139. cls = Unit::getClass(obj.getStringData(), autoload);
  140. if (!cls) {
  141. String err = "class_uses(): Class %s does not exist";
  142. if (autoload) {
  143. err += " and could not be loaded";
  144. }
  145. raise_warning(err.c_str(), obj.toString().c_str());
  146. return false;
  147. }
  148. } else if (obj.isObject()) {
  149. cls = obj.getObjectData()->getVMClass();
  150. } else {
  151. raise_warning("class_uses(): object or string expected");
  152. return false;
  153. }
  154. auto &usedTraits = cls->preClass()->usedTraits();
  155. ArrayInit ret(usedTraits.size(), ArrayInit::Map{});
  156. for (auto const& traitName : usedTraits) {
  157. ret.set(StrNR(traitName), VarNR(traitName));
  158. }
  159. return ret.toArray();
  160. }
  161. #define CHECK_TRAVERSABLE_IMPL(obj, ret) \
  162. if (!obj.isObject() || \
  163. !obj.getObjectData()->instanceof(SystemLib::s_TraversableClass)) { \
  164. raise_recoverable_error("Argument must implement interface Traversable"); \
  165. return ret; \
  166. }
  167. Object get_traversable_object_iterator(const Variant& obj) {
  168. bool isIteratorAggregate;
  169. Object itObj = obj.getObjectData()
  170. ->iterableObject(isIteratorAggregate, true);
  171. if (!isIteratorAggregate) {
  172. if (!obj.getObjectData()->instanceof(
  173. SystemLib::s_IteratorAggregateClass)) {
  174. raise_error("Objects returned by getIterator() must be traversable or "
  175. "implement interface Iterator");
  176. } else {
  177. raise_error(
  178. "Class %s must implement interface Traversable as part of either "
  179. "Iterator or IteratorAggregate",
  180. obj.toObject()->getClassName().data()
  181. );
  182. }
  183. }
  184. return itObj;
  185. }
  186. Variant HHVM_FUNCTION(iterator_apply, const Variant& obj, const Variant& func,
  187. const Array& params /* = null_array */) {
  188. VMRegAnchor _;
  189. CHECK_TRAVERSABLE_IMPL(obj, 0);
  190. Object pobj = get_traversable_object_iterator(obj);
  191. pobj->o_invoke_few_args(s_rewind, 0);
  192. int64_t count = 0;
  193. while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
  194. if (!same(vm_call_user_func(func, params), true)) {
  195. break;
  196. }
  197. ++count;
  198. pobj->o_invoke_few_args(s_next, 0);
  199. }
  200. return count;
  201. }
  202. Variant HHVM_FUNCTION(iterator_count, const Variant& obj) {
  203. VMRegAnchor _;
  204. CHECK_TRAVERSABLE_IMPL(obj, 0);
  205. Object pobj = get_traversable_object_iterator(obj);
  206. pobj->o_invoke_few_args(s_rewind, 0);
  207. int64_t count = 0;
  208. while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
  209. ++count;
  210. pobj->o_invoke_few_args(s_next, 0);
  211. }
  212. return count;
  213. }
  214. Array HHVM_FUNCTION(iterator_to_array, const Variant& obj,
  215. bool use_keys /* = true */) {
  216. VMRegAnchor _;
  217. Array ret(Array::Create());
  218. CHECK_TRAVERSABLE_IMPL(obj, ret);
  219. Object pobj = get_traversable_object_iterator(obj);
  220. pobj->o_invoke_few_args(s_rewind, 0);
  221. while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
  222. Variant val = pobj->o_invoke_few_args(s_current, 0);
  223. if (use_keys) {
  224. Variant key = pobj->o_invoke_few_args(s_key, 0);
  225. ret.set(key, val);
  226. } else {
  227. ret.append(val);
  228. }
  229. pobj->o_invoke_few_args(s_next, 0);
  230. }
  231. return ret;
  232. }
  233. bool HHVM_FUNCTION(spl_autoload_register,
  234. const Variant& autoload_function /* = null_variant */,
  235. bool throws /* = true */,
  236. bool prepend /* = false */) {
  237. if (same(autoload_function, s_spl_autoload_call)) {
  238. if (throws) {
  239. throw_spl_exception("Function spl_autoload_call()"
  240. "cannot be registered");
  241. }
  242. return false;
  243. }
  244. const Variant& func = autoload_function.isNull() ?
  245. s_spl_autoload : autoload_function;
  246. bool res = AutoloadHandler::s_instance->addHandler(func, prepend);
  247. if (!res && throws) {
  248. throw_spl_exception("Invalid autoload_function specified");
  249. }
  250. return res;
  251. }
  252. bool HHVM_FUNCTION(spl_autoload_unregister, const Variant& autoload_function) {
  253. if (same(autoload_function, s_spl_autoload_call)) {
  254. AutoloadHandler::s_instance->removeAllHandlers();
  255. } else {
  256. AutoloadHandler::s_instance->removeHandler(autoload_function);
  257. }
  258. return true;
  259. }
  260. Variant HHVM_FUNCTION(spl_autoload_functions) {
  261. const Array& handlers = AutoloadHandler::s_instance->getHandlers();
  262. if (handlers.isNull()) {
  263. return false;
  264. } else {
  265. return handlers.values();
  266. }
  267. }
  268. void HHVM_FUNCTION(spl_autoload_call, const String& class_name) {
  269. AutoloadHandler::s_instance->autoloadClass(class_name, true);
  270. }
  271. namespace {
  272. struct ExtensionList final : RequestEventHandler {
  273. void requestInit() override {
  274. extensions = make_packed_array(String(".inc"), String(".php"));
  275. }
  276. void requestShutdown() override {
  277. extensions.reset();
  278. }
  279. Array extensions;
  280. };
  281. IMPLEMENT_STATIC_REQUEST_LOCAL(ExtensionList, s_extension_list);
  282. }
  283. String HHVM_FUNCTION(spl_autoload_extensions,
  284. const String& file_extensions /* = null_string */) {
  285. if (!file_extensions.empty()) {
  286. s_extension_list->extensions = StringUtil::Explode(file_extensions, ",")
  287. .toArray();
  288. return file_extensions;
  289. }
  290. return StringUtil::Implode(s_extension_list->extensions, ",");
  291. }
  292. ///////////////////////////////////////////////////////////////////////////////
  293. template <class T>
  294. static SmartPtr<T> getDir(const Object& dir_iter) {
  295. static_assert(std::is_base_of<Directory, T>::value,
  296. "Only cast to directories");
  297. return cast<T>(*dir_iter->o_realProp("dir", 0, s_directory_iterator));
  298. }
  299. static Variant HHVM_METHOD(DirectoryIterator, hh_readdir) {
  300. auto dir = getDir<Directory>(ObjNR(this_).asObject());
  301. if (auto array_dir = dyn_cast<ArrayDirectory>(dir)) {
  302. auto prop = this_->o_realProp("dirName", 0, s_directory_iterator);
  303. *prop = array_dir->path();
  304. }
  305. return HHVM_FN(readdir)(Resource(dir));
  306. }
  307. static int64_t HHVM_METHOD(GlobIterator, count) {
  308. return getDir<ArrayDirectory>(ObjNR(this_).asObject())->size();
  309. }
  310. ///////////////////////////////////////////////////////////////////////////////
  311. class SPLExtension final : public Extension {
  312. public:
  313. SPLExtension() : Extension("spl", "0.2") { }
  314. void moduleLoad(const IniSetting::Map& ini, Hdf config) override {
  315. HHVM_ME(DirectoryIterator, hh_readdir);
  316. HHVM_ME(GlobIterator, count);
  317. }
  318. void moduleInit() override {
  319. HHVM_FE(spl_object_hash);
  320. HHVM_FE(hphp_object_pointer);
  321. HHVM_FE(hphp_get_this);
  322. HHVM_FE(class_implements);
  323. HHVM_FE(class_parents);
  324. HHVM_FE(class_uses);
  325. HHVM_FE(iterator_apply);
  326. HHVM_FE(iterator_count);
  327. HHVM_FE(iterator_to_array);
  328. HHVM_FE(spl_autoload_call);
  329. HHVM_FE(spl_autoload_extensions);
  330. HHVM_FE(spl_autoload_functions);
  331. HHVM_FE(spl_autoload_register);
  332. HHVM_FE(spl_autoload_unregister);
  333. loadSystemlib();
  334. }
  335. } s_SPL_extension;
  336. ///////////////////////////////////////////////////////////////////////////////
  337. }