class_implements should use autoload if specified
[hiphop-php.git] / src / runtime / ext / ext_spl.cpp
blob4e9e916c5d4b3eca49fc791a8eeaca5e04a66d2d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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 <runtime/ext/ext_spl.h>
19 #include <runtime/ext/ext_math.h>
20 #include <runtime/ext/ext_class.h>
22 #include <system/lib/systemlib.h>
24 namespace HPHP {
25 ///////////////////////////////////////////////////////////////////////////////
27 static StaticString s_spl_autoload("spl_autoload");
28 static StaticString s_spl_autoload_call("spl_autoload_call");
29 static StaticString s_default_extensions(".inc,.php");
31 #define SPL_ADD_CLASS(cls) ret.set(#cls, #cls)
33 Array f_spl_classes() {
34 Array ret;
35 SPL_ADD_CLASS(AppendIterator);
36 SPL_ADD_CLASS(ArrayIterator);
37 SPL_ADD_CLASS(ArrayObject);
38 SPL_ADD_CLASS(BadFunctionCallException);
39 SPL_ADD_CLASS(BadMethodCallException);
40 SPL_ADD_CLASS(CachingIterator);
41 SPL_ADD_CLASS(Countable);
42 SPL_ADD_CLASS(DirectoryIterator);
43 SPL_ADD_CLASS(DomainException);
44 SPL_ADD_CLASS(EmptyIterator);
45 SPL_ADD_CLASS(FilesystemIterator);
46 SPL_ADD_CLASS(FilterIterator);
47 SPL_ADD_CLASS(GlobIterator);
48 SPL_ADD_CLASS(InfiniteIterator);
49 SPL_ADD_CLASS(InvalidArgumentException);
50 SPL_ADD_CLASS(IteratorIterator);
51 SPL_ADD_CLASS(LengthException);
52 SPL_ADD_CLASS(LimitIterator);
53 SPL_ADD_CLASS(LogicException);
54 SPL_ADD_CLASS(MultipleIterator);
55 SPL_ADD_CLASS(NoRewindIterator);
56 SPL_ADD_CLASS(OuterIterator);
57 SPL_ADD_CLASS(OutOfBoundsException);
58 SPL_ADD_CLASS(OutOfRangeException);
59 SPL_ADD_CLASS(OverflowException);
60 SPL_ADD_CLASS(ParentIterator);
61 SPL_ADD_CLASS(RangeException);
62 SPL_ADD_CLASS(RecursiveArrayIterator);
63 SPL_ADD_CLASS(RecursiveCachingIterator);
64 SPL_ADD_CLASS(RecursiveDirectoryIterator);
65 SPL_ADD_CLASS(RecursiveFilterIterator);
66 SPL_ADD_CLASS(RecursiveIterator);
67 SPL_ADD_CLASS(RecursiveIteratorIterator);
68 SPL_ADD_CLASS(RecursiveRegexIterator);
69 SPL_ADD_CLASS(RecursiveTreeIterator);
70 SPL_ADD_CLASS(RegexIterator);
71 SPL_ADD_CLASS(RuntimeException);
72 SPL_ADD_CLASS(SeekableIterator);
73 SPL_ADD_CLASS(SplDoublyLinkedList);
74 SPL_ADD_CLASS(SplFileInfo);
75 SPL_ADD_CLASS(SplFileObject);
76 SPL_ADD_CLASS(SplFixedArray);
77 SPL_ADD_CLASS(SplHeap);
78 SPL_ADD_CLASS(SplMinHeap);
79 SPL_ADD_CLASS(SplMaxHeap);
80 SPL_ADD_CLASS(SplObjectStorage);
81 SPL_ADD_CLASS(SplObserver);
82 SPL_ADD_CLASS(SplPriorityQueue);
83 SPL_ADD_CLASS(SplQueue);
84 SPL_ADD_CLASS(SplStack);
85 SPL_ADD_CLASS(SplSubject);
86 SPL_ADD_CLASS(SplTempFileObject);
87 SPL_ADD_CLASS(UnderflowException);
88 SPL_ADD_CLASS(UnexpectedValueException);
89 return ret;
92 void throw_spl_exception(const char *fmt, ...) {
93 va_list ap;
94 va_start(ap, fmt);
95 std::string msg;
96 Util::string_vsnprintf(msg, fmt, ap);
97 va_end(ap);
99 throw Object(SystemLib::AllocExceptionObject(Variant(msg)));
102 static bool s_inited = false;
103 static int64 s_hash_mask_handle = 0;
104 static Mutex s_mutex;
106 String f_spl_object_hash(CObjRef obj) {
107 if (!s_inited) {
108 Lock lock(s_mutex);
109 if (!s_inited) {
110 f_mt_srand();
111 s_hash_mask_handle |= f_mt_rand(); s_hash_mask_handle <<= 16;
112 s_hash_mask_handle |= f_mt_rand(); s_hash_mask_handle <<= 16;
113 s_hash_mask_handle |= f_mt_rand(); s_hash_mask_handle <<= 16;
114 s_hash_mask_handle |= f_mt_rand();
115 s_inited = true;
119 char buf[33];
120 snprintf(buf, sizeof(buf), "%032llx", s_hash_mask_handle ^ (int64)obj.get());
121 return String(buf, CopyString);
124 Variant f_hphp_get_this() {
125 if (hhvm) {
126 return g_vmContext->getThis();
127 } else {
128 return FrameInjection::GetThis();
132 Variant f_class_implements(CVarRef obj, bool autoload /* = true */) {
133 String clsname;
134 if (obj.isString()) {
135 clsname = obj.toString();
136 } else if (obj.isObject()) {
137 clsname = obj.toObject()->o_getClassName();
138 } else {
139 return false;
142 const ClassInfo *info = ClassInfo::FindClassInterfaceOrTrait(clsname);
143 if (info == NULL) {
144 if (!autoload) return false;
145 AutoloadHandler::s_instance->invokeHandler(clsname);
146 return f_class_implements(clsname, false);
149 Array ret(Array::Create());
150 ClassInfo::InterfaceVec ifs;
151 info->getAllInterfacesVec(ifs);
152 for (unsigned int i = 0; i < ifs.size(); i++) {
153 ret.set(ifs[i], ifs[i]);
156 return ret;
159 Variant f_class_parents(CVarRef obj, bool autoload /* = true */) {
160 String clsname;
161 if (obj.isString()) {
162 clsname = obj.toString();
163 } else if (obj.isObject()) {
164 clsname = obj.toObject()->o_getClassName();
165 } else {
166 return false;
169 const ClassInfo *info = ClassInfo::FindClassInterfaceOrTrait(clsname);
170 if (info == NULL) {
171 if (!autoload) return false;
172 AutoloadHandler::s_instance->invokeHandler(clsname);
173 return f_class_parents(clsname, false);
176 Array ret(Array::Create());
177 ClassInfo::ClassVec parents;
178 info->getAllParentsVec(parents);
179 for (unsigned int i = 0; i < parents.size(); i++) {
180 ret.set(parents[i], parents[i]);
183 return ret;
186 Variant f_class_uses(CVarRef obj, bool autoload /* = true */) {
187 String clsname;
188 if (obj.isString()) {
189 clsname = obj.toString();
190 } else if (obj.isObject()) {
191 clsname = obj.toObject()->o_getClassName();
192 } else {
193 return false;
196 const ClassInfo *info = ClassInfo::FindClassInterfaceOrTrait(clsname);
197 if (!info) {
198 if (!autoload) return false;
199 AutoloadHandler::s_instance->invokeHandler(clsname);
200 return f_class_uses(clsname, false);
203 Array ret(Array::Create());
204 const ClassInfo::TraitVec &traits = info->getTraitsVec();
205 for (unsigned int i = 0; i < traits.size(); i++) {
206 ret.set(traits[i], traits[i]);
209 return ret;
212 Variant f_iterator_apply(CVarRef obj, CVarRef func,
213 CArrRef params /* = null_array */) {
214 if (!obj.instanceof("Traversable")) {
215 return false;
217 Object pobj = obj.toObject();
218 pobj->o_invoke("rewind", null_array, -1);
219 int64 count = 0;
220 while (same(pobj->o_invoke("valid", null_array, -1), true)) {
221 if (!same(f_call_user_func_array(func, params), true)) {
222 break;
224 ++count;
225 pobj->o_invoke("next", null_array, -1);
227 return count;
230 Variant f_iterator_count(CVarRef obj) {
231 if (!obj.instanceof("Traversable")) {
232 return false;
234 Object pobj = obj.toObject();
235 pobj->o_invoke("rewind", null_array, -1);
236 int64 count = 0;
237 while (same(pobj->o_invoke("valid", null_array, -1), true)) {
238 ++count;
239 pobj->o_invoke("next", null_array, -1);
241 return count;
244 Variant f_iterator_to_array(CVarRef obj, bool use_keys /* = true */) {
245 if (!obj.instanceof("Traversable")) {
246 return false;
248 Array ret(Array::Create());
249 Object pobj = obj.toObject();
250 pobj->o_invoke("rewind", null_array, -1);
251 while (same(pobj->o_invoke("valid", null_array, -1), true)) {
252 Variant val = pobj->o_invoke("current", null_array, -1);
253 if (use_keys) {
254 Variant key = pobj->o_invoke("key", null_array, -1);
255 ret.set(key, val);
256 } else {
257 ret.append(val);
259 pobj->o_invoke("next", null_array, -1);
261 return ret;
264 bool f_spl_autoload_register(CVarRef autoload_function /* = null_variant */,
265 bool throws /* = true */,
266 bool prepend /* = false */) {
267 if (autoload_function.same(s_spl_autoload_call)) {
268 if (throws) {
269 throw_spl_exception("Function spl_autoload_call()"
270 "cannot be registered");
272 return false;
274 CVarRef func = autoload_function.isNull() ?
275 s_spl_autoload : autoload_function;
276 bool res = AutoloadHandler::s_instance->addHandler(func, prepend);
277 if (!res && throws) {
278 throw_spl_exception("Invalid autoload_function specified");
280 return res;
283 bool f_spl_autoload_unregister(CVarRef autoload_function) {
284 if (autoload_function.same(s_spl_autoload_call)) {
285 AutoloadHandler::s_instance->removeAllHandlers();
286 } else {
287 AutoloadHandler::s_instance->removeHandler(autoload_function);
289 return true;
292 Variant f_spl_autoload_functions() {
293 CArrRef handlers = AutoloadHandler::s_instance->getHandlers();
294 if (handlers.isNull())
295 return false;
296 else
297 return handlers.values();
300 void f_spl_autoload_call(CStrRef class_name) {
301 AutoloadHandler::s_instance->invokeHandler(class_name, NULL, true);
304 namespace {
305 class ExtensionList : public RequestEventHandler {
306 public:
307 virtual void requestInit() {
308 extensions = CREATE_VECTOR2(String(".inc"), String(".php"));
310 virtual void requestShutdown() {
311 extensions.reset();
314 Array extensions;
317 IMPLEMENT_STATIC_REQUEST_LOCAL(ExtensionList, s_extension_list);
320 String f_spl_autoload_extensions(CStrRef file_extensions /* = null_string */) {
321 if (!file_extensions.isNull()) {
322 s_extension_list->extensions = StringUtil::Explode(file_extensions, ",")
323 .toArray();
324 return file_extensions;
326 return StringUtil::Implode(s_extension_list->extensions, ",");
329 void f_spl_autoload(CStrRef class_name,
330 CStrRef file_extensions /* = null_string */) {
331 Array ext = file_extensions.isNull()
332 ? s_extension_list->extensions
333 : StringUtil::Explode(file_extensions, ",").toArray();
334 String lClass = StringUtil::ToLower(class_name);
335 Globals *g = get_globals();
336 bool found = false;
337 for (ArrayIter iter(ext); iter; ++iter) {
338 String fileName = lClass + iter.second();
339 include(fileName, true, g, "", false);
340 if (f_class_exists(class_name, false)) {
341 found = true;
342 break;
346 if (!found && !AutoloadHandler::s_instance->isRunning()) {
347 throw_spl_exception("Class %s could not be loaded", class_name.c_str());
351 ///////////////////////////////////////////////////////////////////////////////