Arquivos
hhvm/hphp/runtime/base/file/user_file.cpp
T
2013-03-18 16:05:53 -07:00

300 linhas
9.7 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <runtime/base/file/user_file.h>
#include <runtime/ext/ext_function.h>
#include <runtime/vm/instance.h>
#include <runtime/vm/translator/translator-inline.h>
namespace HPHP {
IMPLEMENT_OBJECT_ALLOCATION(UserFile)
///////////////////////////////////////////////////////////////////////////////
StaticString UserFile::s_class_name("UserFile");
StaticString s_stream_open("stream_open");
StaticString s_stream_close("stream_close");
StaticString s_stream_read("stream_read");
StaticString s_stream_write("stream_write");
StaticString s_stream_seek("stream_seek");
StaticString s_stream_tell("stream_tell");
StaticString s_stream_eof("stream_eof");
StaticString s_stream_flush("stream_flush");
StaticString s_stream_lock("stream_lock");
StaticString s_call("__call");
///////////////////////////////////////////////////////////////////////////////
UserFile::UserFile(VM::Class *cls, int options /*= 0 */,
CVarRef context /*= null */) :
m_cls(cls), m_options(options) {
VM::Transl::VMRegAnchor _;
const VM::Func *ctor;
if (MethodLookup::MethodFoundWithThis !=
g_vmContext->lookupCtorMethod(ctor, cls)) {
throw InvalidArgumentException(0, "Unable to call %s's constructor",
cls->name()->data());
}
m_obj = VM::Instance::newInstance(cls);
m_obj.o_setPublic("context", context);
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), ctor,
Array::Create(), m_obj.get());
m_StreamOpen = lookupMethod(s_stream_open.get());
m_StreamClose = lookupMethod(s_stream_close.get());
m_StreamRead = lookupMethod(s_stream_read.get());
m_StreamWrite = lookupMethod(s_stream_write.get());
m_StreamSeek = lookupMethod(s_stream_seek.get());
m_StreamTell = lookupMethod(s_stream_tell.get());
m_StreamEof = lookupMethod(s_stream_eof.get());
m_StreamFlush = lookupMethod(s_stream_flush.get());
m_StreamLock = lookupMethod(s_stream_lock.get());
m_Call = lookupMethod(s_call.get());
}
UserFile::~UserFile() {
}
const VM::Func* UserFile::lookupMethod(const StringData* name) {
const VM::Func *f = m_cls->lookupMethod(name);
if (!f) return nullptr;
if (f->attrs() & VM::AttrStatic) {
throw InvalidArgumentException(0, "%s::%s() must not be declared static",
m_cls->name()->data(), name->data());
}
return f;
}
///////////////////////////////////////////////////////////////////////////////
Variant UserFile::invoke(const VM::Func *func, CStrRef name,
CArrRef args, bool &success) {
VM::Transl::VMRegAnchor _;
// Assume failure
success = false;
// Public method, no private ancestor, no need for further checks (common)
if (func &&
!(func->attrs() & (VM::AttrPrivate|VM::AttrProtected|VM::AttrAbstract)) &&
!func->hasPrivateAncestor()) {
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), func, args, m_obj.get());
success = true;
return ret;
}
// No explicitly defined function, no __call() magic method
// Give up.
if (!func && !m_Call) {
return uninit_null();
}
switch(g_vmContext->lookupObjMethod(func, m_cls, name.get())) {
case MethodLookup::MethodFoundWithThis:
{
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), func, args, m_obj.get());
success = true;
return ret;
}
case MethodLookup::MagicCallFound:
{
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), func,
CREATE_VECTOR2(name, args), m_obj.get());
success = true;
return ret;
}
case MethodLookup::MethodNotFound:
// There's a method somewhere in the heirarchy, but none
// which are accessible.
/* fallthrough */
case MethodLookup::MagicCallStaticFound:
// We're not calling staticly, so this result is unhelpful
// Also, it's never produced by lookupObjMethod, so it'll
// never happen, but we must handle all enums
return uninit_null();
case MethodLookup::MethodFoundNoThis:
// Should never happen (Attr::Static check in ctor)
assert(false);
raise_error("%s::%s() must not be declared static",
m_cls->name()->data(), name.data());
return uninit_null();
}
NOT_REACHED();
return uninit_null();
}
///////////////////////////////////////////////////////////////////////////////
bool UserFile::open(CStrRef filename, CStrRef mode) {
// bool stream_open($path, $mode, $options, &$opened_path)
bool success = false;
Variant opened_path;
Variant ret = invoke(m_StreamOpen, s_stream_open, CREATE_VECTOR4(
filename, mode, m_options, ref(opened_path)), success);
if (success && (ret.toBoolean() == true)) {
return true;
}
raise_warning("\"%s::stream_open\" call failed", m_cls->name()->data());
return false;
}
bool UserFile::close() {
// PHP's streams layer explicitly flushes on close
// Mimick that for user-wrappers by pushing the flush here
// without impacting other HPHP stream types.
flush();
// void stream_close()
invoke(m_StreamClose, s_stream_close, Array::Create());
return true;
}
///////////////////////////////////////////////////////////////////////////////
int64_t UserFile::readImpl(char *buffer, int64_t length) {
// String stread_read($count)
bool success = false;
String str = invoke(m_StreamRead, s_stream_read,
CREATE_VECTOR1(length), success);
if (!success) {
raise_warning("%s::stream_read is not implemented",
m_cls->name()->data());
return 0;
}
int64_t didread = str.size();
if (didread > length) {
raise_warning("%s::stream_read - read %ld bytes more data than requested "
"(%ld read, %ld max) - excess data will be lost",
m_cls->name()->data(), (long)(didread - length),
(long)didread, (long)length);
didread = length;
}
memcpy(buffer, str.data(), didread);
return didread;
}
int64_t UserFile::writeImpl(const char *buffer, int64_t length) {
// stream_write($data)
bool success = false;
int64_t didWrite = invoke(m_StreamWrite, s_stream_write,
CREATE_VECTOR1(String(buffer, length, CopyString)),
success);
if (!success) {
raise_warning("%s::stream_write is not implemented",
m_cls->name()->data());
return 0;
}
if (didWrite > length) {
raise_warning("%s::stream_write - wrote %ld bytes more data than "
"requested (%ld written, %ld max)",
m_cls->name()->data(), (long)(didWrite - length),
(long)didWrite, (long)length);
didWrite = length;
}
return didWrite;
}
///////////////////////////////////////////////////////////////////////////////
bool UserFile::seek(int64_t offset, int whence /* = SEEK_SET */) {
// bool stream_seek($offset, $whence)
bool success = false;
bool sought = invoke(m_StreamSeek, s_stream_seek,
CREATE_VECTOR2(offset, whence), success);
return success ? sought : false;
}
int64_t UserFile::tell() {
// int stream_tell()
bool success = false;
Variant ret = invoke(m_StreamTell, s_stream_tell, Array::Create(), success);
if (!success) {
raise_warning("%s::stream_tell is not implemented!", m_cls->name()->data());
return -1;
}
return ret.isInteger() ? ret.toInt64() : -1;
}
bool UserFile::eof() {
// If there's data in the read buffer, then we're clearly not EOF
if ((m_writepos - m_readpos) > 0) {
return false;
}
// bool stream_eof()
bool success = false;
Variant ret = invoke(m_StreamEof, s_stream_eof, Array::Create(), success);
if (!success) {
return false;
}
return ret.isBoolean() ? ret.toBoolean() : true;
}
bool UserFile::flush() {
// bool stream_flush()
bool success = false;
Variant ret = invoke(m_StreamFlush, s_stream_flush,
Array::Create(), success);
if (!success) {
return false;
}
return ret.isBoolean() ? ret.toBoolean() : false;
}
bool UserFile::lock(int operation, bool &wouldBlock) {
int64_t op = 0;
if (operation & LOCK_NB) {
op |= k_LOCK_NB;
}
switch (operation & ~LOCK_NB) {
case LOCK_SH: op |= k_LOCK_SH; break;
case LOCK_EX: op |= k_LOCK_EX; break;
case LOCK_UN: op |= k_LOCK_UN; break;
}
// bool stream_lock(int $operation)
bool success = false;
Variant ret = invoke(m_StreamLock, s_stream_lock,
CREATE_VECTOR1(op), success);
if (!success) {
if (operation) {
raise_warning("%s::stream_lock is not implemented!",
m_cls->name()->data());
}
return false;
}
return ret.isBoolean() ? ret.toBoolean() : false;
}
///////////////////////////////////////////////////////////////////////////////
}