SPL + filter extensions should report implemented
[hiphop-php.git] / hphp / runtime / ext / ext_spl.cpp
blob95ebf14a66f58e43f311045d2ab316535a9a34d8
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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/ext_spl.h"
19 #include "hphp/runtime/ext/ext_math.h"
20 #include "hphp/runtime/ext/ext_class.h"
22 #include "hphp/system/systemlib.h"
24 namespace HPHP {
25 IMPLEMENT_DEFAULT_EXTENSION(SPL);
26 ///////////////////////////////////////////////////////////////////////////////
28 const StaticString
29 s_spl_autoload("spl_autoload"),
30 s_spl_autoload_call("spl_autoload_call"),
31 s_default_extensions(".inc,.php"),
32 s_rewind("rewind"),
33 s_valid("valid"),
34 s_next("next"),
35 s_current("current"),
36 s_key("key"),
37 s_getIterator("getIterator");
39 const StaticString spl_classes[] = {
40 StaticString("AppendIterator"),
41 StaticString("ArrayIterator"),
42 StaticString("ArrayObject"),
43 StaticString("BadFunctionCallException"),
44 StaticString("BadMethodCallException"),
45 StaticString("CachingIterator"),
46 StaticString("Countable"),
47 StaticString("DirectoryIterator"),
48 StaticString("DomainException"),
49 StaticString("EmptyIterator"),
50 StaticString("FilesystemIterator"),
51 StaticString("FilterIterator"),
52 StaticString("GlobIterator"),
53 StaticString("InfiniteIterator"),
54 StaticString("InvalidArgumentException"),
55 StaticString("IteratorIterator"),
56 StaticString("LengthException"),
57 StaticString("LimitIterator"),
58 StaticString("LogicException"),
59 StaticString("MultipleIterator"),
60 StaticString("NoRewindIterator"),
61 StaticString("OuterIterator"),
62 StaticString("OutOfBoundsException"),
63 StaticString("OutOfRangeException"),
64 StaticString("OverflowException"),
65 StaticString("ParentIterator"),
66 StaticString("RangeException"),
67 StaticString("RecursiveArrayIterator"),
68 StaticString("RecursiveCachingIterator"),
69 StaticString("RecursiveDirectoryIterator"),
70 StaticString("RecursiveFilterIterator"),
71 StaticString("RecursiveIterator"),
72 StaticString("RecursiveIteratorIterator"),
73 StaticString("RecursiveRegexIterator"),
74 StaticString("RecursiveTreeIterator"),
75 StaticString("RegexIterator"),
76 StaticString("RuntimeException"),
77 StaticString("SeekableIterator"),
78 StaticString("SplDoublyLinkedList"),
79 StaticString("SplFileInfo"),
80 StaticString("SplFileObject"),
81 StaticString("SplFixedArray"),
82 StaticString("SplHeap"),
83 StaticString("SplMinHeap"),
84 StaticString("SplMaxHeap"),
85 StaticString("SplObjectStorage"),
86 StaticString("SplObserver"),
87 StaticString("SplPriorityQueue"),
88 StaticString("SplQueue"),
89 StaticString("SplStack"),
90 StaticString("SplSubject"),
91 StaticString("SplTempFileObject"),
92 StaticString("UnderflowException"),
93 StaticString("UnexpectedValueException"),
96 Array f_spl_classes() {
97 const size_t num_classes = sizeof(spl_classes) / sizeof(spl_classes[0]);
98 ArrayInit ret(num_classes);
99 for (size_t i = 0; i < num_classes; ++i) {
100 ret.set(spl_classes[i], spl_classes[i]);
102 return ret.create();
105 void throw_spl_exception(const char *fmt, ...) ATTRIBUTE_PRINTF(1,2);
106 void throw_spl_exception(const char *fmt, ...) {
107 va_list ap;
108 va_start(ap, fmt);
109 std::string msg;
110 Util::string_vsnprintf(msg, fmt, ap);
111 va_end(ap);
113 throw Object(SystemLib::AllocExceptionObject(Variant(msg)));
116 static bool s_inited = false;
117 static int64_t s_hash_mask_handle = 0;
118 static Mutex s_mutex;
120 String f_spl_object_hash(CObjRef obj) {
121 if (!s_inited) {
122 Lock lock(s_mutex);
123 if (!s_inited) {
124 f_mt_srand();
125 s_hash_mask_handle |= f_mt_rand(); s_hash_mask_handle <<= 16;
126 s_hash_mask_handle |= f_mt_rand(); s_hash_mask_handle <<= 16;
127 s_hash_mask_handle |= f_mt_rand(); s_hash_mask_handle <<= 16;
128 s_hash_mask_handle |= f_mt_rand();
129 s_inited = true;
133 char buf[33];
134 snprintf(buf, sizeof(buf), "%032" PRIx64,
135 s_hash_mask_handle ^ (int64_t)obj.get());
136 return String(buf, CopyString);
139 int64_t f_hphp_object_pointer(CObjRef obj) { return (int64_t)obj.get();}
141 Variant f_hphp_get_this() {
142 return g_vmContext->getThis();
145 Variant f_class_implements(CVarRef obj, bool autoload /* = true */) {
146 Class* cls;
147 if (obj.isString()) {
148 cls = Unit::getClass(obj.getStringData(), autoload);
149 if (!cls) {
150 return false;
152 } else if (obj.isObject()) {
153 cls = obj.getObjectData()->getVMClass();
154 } else {
155 return false;
157 Array ret(Array::Create());
158 const Class::InterfaceMap& ifaces = cls->allInterfaces();
159 for (int i = 0, size = ifaces.size(); i < size; i++) {
160 ret.set(ifaces[i]->nameRef(), ifaces[i]->nameRef());
162 return ret;
165 Variant f_class_parents(CVarRef obj, bool autoload /* = true */) {
166 Class* cls;
167 if (obj.isString()) {
168 cls = Unit::getClass(obj.getStringData(), autoload);
169 if (!cls) {
170 return false;
172 } else if (obj.isObject()) {
173 cls = obj.getObjectData()->getVMClass();
174 } else {
175 return false;
177 Array ret(Array::Create());
178 for (cls = cls->parent(); cls; cls = cls->parent()) {
179 auto& clsName = cls->nameRef();
180 ret.set(clsName, clsName);
182 return ret;
185 Variant f_class_uses(CVarRef obj, bool autoload /* = true */) {
186 Class* cls;
187 if (obj.isString()) {
188 cls = Unit::getClass(obj.getStringData(), autoload);
189 if (!cls) {
190 return false;
192 } else if (obj.isObject()) {
193 cls = obj.getObjectData()->getVMClass();
194 } else {
195 return false;
197 Array ret(Array::Create());
198 for (auto& elem : cls->usedTraits()) {
199 auto& traitName = elem->nameRef();
200 ret.set(traitName, traitName);
202 return ret;
205 Object get_traversable_object_iterator(CVarRef obj) {
206 if (!obj.instanceof(SystemLib::s_TraversableClass)) {
207 raise_error("Argument must implement interface Traversable");
210 bool isIteratorAggregate;
211 Object itObj = obj.getObjectData()
212 ->iterableObject(isIteratorAggregate, true);
214 if (!isIteratorAggregate) {
215 if (obj.instanceof(SystemLib::s_IteratorAggregateClass)) {
216 raise_error("Objects returned by getIterator() must be traversable or "
217 "implement interface Iterator");
218 } else {
219 raise_error(
220 "Class %s must implement interface Traversable as part of either "
221 "Iterator or IteratorAggregate",
222 obj.toObject()->o_getClassName()->data()
227 return itObj;
230 Variant f_iterator_apply(CVarRef obj, CVarRef func,
231 CArrRef params /* = null_array */) {
232 Object pobj = get_traversable_object_iterator(obj);
233 pobj->o_invoke_few_args(s_rewind, 0);
234 int64_t count = 0;
235 while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
236 if (!same(vm_call_user_func(func, params), true)) {
237 break;
239 ++count;
240 pobj->o_invoke_few_args(s_next, 0);
242 return count;
245 Variant f_iterator_count(CVarRef obj) {
246 Object pobj = get_traversable_object_iterator(obj);
247 pobj->o_invoke_few_args(s_rewind, 0);
248 int64_t count = 0;
249 while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
250 ++count;
251 pobj->o_invoke_few_args(s_next, 0);
253 return count;
256 Variant f_iterator_to_array(CVarRef obj, bool use_keys /* = true */) {
257 Object pobj = get_traversable_object_iterator(obj);
258 Array ret(Array::Create());
260 pobj->o_invoke_few_args(s_rewind, 0);
261 while (same(pobj->o_invoke_few_args(s_valid, 0), true)) {
262 Variant val = pobj->o_invoke_few_args(s_current, 0);
263 if (use_keys) {
264 Variant key = pobj->o_invoke_few_args(s_key, 0);
265 ret.set(key, val);
266 } else {
267 ret.append(val);
269 pobj->o_invoke_few_args(s_next, 0);
271 return ret;
274 bool f_spl_autoload_register(CVarRef autoload_function /* = null_variant */,
275 bool throws /* = true */,
276 bool prepend /* = false */) {
277 if (same(autoload_function, s_spl_autoload_call)) {
278 if (throws) {
279 throw_spl_exception("Function spl_autoload_call()"
280 "cannot be registered");
282 return false;
284 CVarRef func = autoload_function.isNull() ?
285 s_spl_autoload : autoload_function;
286 bool res = AutoloadHandler::s_instance->addHandler(func, prepend);
287 if (!res && throws) {
288 throw_spl_exception("Invalid autoload_function specified");
290 return res;
293 bool f_spl_autoload_unregister(CVarRef autoload_function) {
294 if (same(autoload_function, s_spl_autoload_call)) {
295 AutoloadHandler::s_instance->removeAllHandlers();
296 } else {
297 AutoloadHandler::s_instance->removeHandler(autoload_function);
299 return true;
302 Variant f_spl_autoload_functions() {
303 CArrRef handlers = AutoloadHandler::s_instance->getHandlers();
304 if (handlers.isNull())
305 return false;
306 else
307 return handlers.values();
310 void f_spl_autoload_call(CStrRef class_name) {
311 AutoloadHandler::s_instance->invokeHandler(class_name, true);
314 namespace {
315 class ExtensionList : public RequestEventHandler {
316 public:
317 virtual void requestInit() {
318 extensions = CREATE_VECTOR2(String(".inc"), String(".php"));
320 virtual void requestShutdown() {
321 extensions.reset();
324 Array extensions;
327 IMPLEMENT_STATIC_REQUEST_LOCAL(ExtensionList, s_extension_list);
330 String f_spl_autoload_extensions(CStrRef file_extensions /* = null_string */) {
331 if (!file_extensions.isNull()) {
332 s_extension_list->extensions = StringUtil::Explode(file_extensions, ",")
333 .toArray();
334 return file_extensions;
336 return StringUtil::Implode(s_extension_list->extensions, ",");
339 void f_spl_autoload(CStrRef class_name,
340 CStrRef file_extensions /* = null_string */) {
341 Array ext = file_extensions.isNull()
342 ? s_extension_list->extensions
343 : StringUtil::Explode(file_extensions, ",").toArray();
344 String lClass = StringUtil::ToLower(class_name);
345 bool found = false;
346 for (ArrayIter iter(ext); iter; ++iter) {
347 String fileName = lClass + iter.second().toString();
348 include(fileName, true, "", false);
349 if (f_class_exists(class_name, false)) {
350 found = true;
351 break;
355 if (!found && !AutoloadHandler::s_instance->isRunning()) {
356 throw_spl_exception("Class %s could not be loaded", class_name.c_str());
360 ///////////////////////////////////////////////////////////////////////////////