Skip to content

Commit 57fc87f

Browse files
committed
Put PhpRequestWorker/AsyncMessageWorker/AsyncMapperChannel in their own files.
Remove most of the implementation of these classes from their `.h` files and create new `.cc` files for them. This makes `node_php_embed.cc` more focused on implementing the PHP SAPI and Node module interface, with most of the other stuff moved to `phprequestworker.cc`.
1 parent 7eba98c commit 57fc87f

12 files changed

+779
-571
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ Triggers a PHP "request", and returns a [`Promise`] which will be
7575
resolved when the request completes. If you prefer to use callbacks,
7676
you can ignore the return value and pass a callback as the second
7777
parameter.
78-
* `options`: a hash containing various parameters for the request.
78+
* `options`: an object containing various parameters for the request.
7979
Either `source` or `file` is mandatory; the rest are optional.
8080
- `source`:
8181
Specifies a source string to evaluate *as an expression* in

binding.gyp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
],
2929
},
3030
'sources': [
31+
'src/asyncmapperchannel.cc',
32+
'src/asyncmessageworker.cc',
33+
'src/phprequestworker.cc',
3134
'src/node_php_embed.cc',
3235
'src/node_php_jsbuffer_class.cc',
3336
'src/node_php_jsobject_class.cc',

src/asyncmapperchannel.cc

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// AsyncMapperChannel is an implementation of MapperChannel used by
2+
// AsyncMessageWorker.
3+
4+
// Copyright (c) 2015 C. Scott Ananian <cscott@cscott.net>
5+
#include "src/asyncmapperchannel.h"
6+
7+
#include <cassert>
8+
#include <unordered_map>
9+
#include <vector>
10+
11+
#include "nan.h"
12+
13+
extern "C" {
14+
#include "main/php.h"
15+
}
16+
17+
#include "src/asyncmessageworker.h"
18+
#include "src/values.h" // for objid_t
19+
20+
namespace node_php_embed {
21+
22+
namespace amw {
23+
24+
// AsyncMapperChannel implementation.
25+
26+
// JsObjectMapper interface -----------------------
27+
// Callable only from the JavaScript side.
28+
29+
// Map Js object to an index.
30+
objid_t AsyncMapperChannel::IdForJsObj(const v8::Local<v8::Object> o) {
31+
// Have we already mapped this?
32+
Nan::HandleScope scope;
33+
v8::Local<v8::NativeWeakMap> jsObjToId = Nan::New(js_obj_to_id_);
34+
if (jsObjToId->Has(o)) {
35+
return Nan::To<objid_t>(jsObjToId->Get(o)).FromJust();
36+
}
37+
38+
// XXX If o is a Promise, then call PrFunPromise.resolve(o),
39+
// and set both of them in the jsObjToId map. This would ensure
40+
// that Promise#nodify is available from PHP.
41+
42+
objid_t id = NewId();
43+
jsObjToId->Set(o, Nan::New(id));
44+
worker_->SaveToPersistent(id, o);
45+
return id;
46+
}
47+
48+
// Map index to JS object (or create it if necessary).
49+
v8::Local<v8::Object> AsyncMapperChannel::JsObjForId(objid_t id) {
50+
Nan::EscapableHandleScope scope;
51+
v8::Local<v8::Value> v = worker_->GetFromPersistent(id);
52+
if (v->IsObject()) {
53+
return scope.Escape(Nan::To<v8::Object>(v).ToLocalChecked());
54+
}
55+
if (!IsValid()) {
56+
// This happens when we return an object at the tail of the request.
57+
return scope.Escape(PhpObject::Create(nullptr, 0));
58+
}
59+
// Make a wrapper!
60+
v8::Local<v8::NativeWeakMap> jsObjToId = Nan::New(js_obj_to_id_);
61+
v8::Local<v8::Object> o = PhpObject::Create(this, id);
62+
jsObjToId->Set(o, Nan::New(id));
63+
worker_->SaveToPersistent(id, o);
64+
return scope.Escape(o);
65+
}
66+
67+
// Free JS references associated with an id.
68+
void AsyncMapperChannel::ClearJsId(objid_t id) {
69+
Nan::HandleScope scope;
70+
v8::Local<v8::Value> v = worker_->GetFromPersistent(id);
71+
if (!v->IsObject()) { return; }
72+
v8::Local<v8::Object> o = Nan::To<v8::Object>(v).ToLocalChecked();
73+
// There might be other live references to this object; set its
74+
// id to 0 to neuter it.
75+
PhpObject::MaybeNeuter(this, o);
76+
// Remove it from our maps (and release our persistent reference).
77+
v8::Local<v8::NativeWeakMap> jsObjToId = Nan::New(js_obj_to_id_);
78+
jsObjToId->Delete(o);
79+
worker_->DeleteFromPersistent(id);
80+
}
81+
82+
objid_t AsyncMapperChannel::ClearAllJsIds() {
83+
uv_mutex_lock(&id_lock_);
84+
objid_t last = next_id_;
85+
next_id_ = 0; // Don't allocate any more ids.
86+
uv_mutex_unlock(&id_lock_);
87+
88+
for (objid_t id = 1; id < last; id++) {
89+
ClearJsId(id);
90+
}
91+
return last;
92+
}
93+
94+
// PhpObjectMapper interface -----------------------
95+
// Callable only from the PHP side.
96+
97+
// Map PHP object to an index.
98+
objid_t AsyncMapperChannel::IdForPhpObj(zval *z) {
99+
assert(Z_TYPE_P(z) == IS_OBJECT);
100+
zend_object_handle handle = Z_OBJ_HANDLE_P(z);
101+
if (php_obj_to_id_.count(handle)) {
102+
return php_obj_to_id_.at(handle);
103+
}
104+
105+
objid_t id = NewId();
106+
if (id >= php_obj_list_.size()) { php_obj_list_.resize(id + 1); }
107+
// XXX Should we clone/separate z?
108+
Z_ADDREF_P(z);
109+
php_obj_list_[id] = z;
110+
php_obj_to_id_[handle] = id;
111+
return id;
112+
}
113+
114+
// Returned value is owned by the object mapper, caller should not release it.
115+
zval *AsyncMapperChannel::PhpObjForId(objid_t id TSRMLS_DC) {
116+
if (id >= php_obj_list_.size()) { php_obj_list_.resize(id + 1); }
117+
ZVal z(php_obj_list_[id] ZEND_FILE_LINE_CC);
118+
if (z.IsNull()) {
119+
node_php_jsobject_create(z.Ptr(), this, id TSRMLS_CC);
120+
php_obj_list_[id] = z.Ptr();
121+
php_obj_to_id_[Z_OBJ_HANDLE_P(z.Ptr())] = id;
122+
// One excess reference, owned by objectmapper.
123+
return z.Escape();
124+
}
125+
// Don't increment reference.
126+
return z.Ptr();
127+
}
128+
129+
// Free PHP references associated with an id.
130+
void AsyncMapperChannel::ClearPhpId(objid_t id TSRMLS_DC) {
131+
zval *z = (id < php_obj_list_.size()) ? php_obj_list_[id] : nullptr;
132+
if (z) {
133+
node_php_jsobject_maybe_neuter(z TSRMLS_CC);
134+
php_obj_list_[id] = nullptr;
135+
php_obj_to_id_.erase(Z_OBJ_HANDLE_P(z));
136+
zval_ptr_dtor(&z);
137+
}
138+
}
139+
140+
// ObjectMapper interface -----------------------
141+
// Callable from both threads.
142+
143+
objid_t AsyncMapperChannel::NewId() {
144+
uv_mutex_lock(&id_lock_);
145+
// next_id_ is 0 if we're shutting down.
146+
objid_t id = (next_id_ == 0) ? 0 : (next_id_++);
147+
uv_mutex_unlock(&id_lock_);
148+
return id;
149+
}
150+
151+
bool AsyncMapperChannel::IsValid() {
152+
uv_mutex_lock(&id_lock_);
153+
bool valid = (next_id_ != 0);
154+
uv_mutex_unlock(&id_lock_);
155+
return valid;
156+
}
157+
158+
// JsMessageChannel interface -----------------------
159+
// Callable only from the PHP side.
160+
void AsyncMapperChannel::SendToJs(Message *m, MessageFlags flags
161+
TSRMLS_DC) const {
162+
worker_->SendToJs(m, flags TSRMLS_CC);
163+
}
164+
// PhpMessageChannel interface -----------------------
165+
// Callable only from the JS side.
166+
void AsyncMapperChannel::SendToPhp(Message *m, MessageFlags flags) const {
167+
worker_->SendToPhp(m, flags);
168+
}
169+
170+
} // namespace amw
171+
172+
} // namespace node_php_embed

src/asyncmapperchannel.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// AsyncMapperChannel is an implementation of MapperChannel used by
2+
// AsyncMessageWorker.
3+
4+
// Copyright (c) 2015 C. Scott Ananian <cscott@cscott.net>
5+
#ifndef NODE_PHP_EMBED_ASYNCMAPPERCHANNEL_H_
6+
#define NODE_PHP_EMBED_ASYNCMAPPERCHANNEL_H_
7+
8+
#include <unordered_map>
9+
#include <vector>
10+
11+
#include "nan.h"
12+
13+
extern "C" {
14+
#include "main/php.h"
15+
}
16+
17+
#include "src/messages.h" // for MapperChannel
18+
#include "src/values.h" // for objid_t
19+
20+
namespace node_php_embed {
21+
22+
class AsyncMessageWorker;
23+
class JsCleanupSyncMsg;
24+
25+
namespace amw {
26+
27+
/* This is the interface exposed to the object proxy classes. */
28+
class AsyncMapperChannel : public MapperChannel {
29+
friend class node_php_embed::AsyncMessageWorker;
30+
friend class node_php_embed::JsCleanupSyncMsg;
31+
public:
32+
virtual ~AsyncMapperChannel() {
33+
// zvals should have been freed beforehand from php_obj_list_ because
34+
// PHP context is shut down so we can't do that now.
35+
js_obj_to_id_.Reset();
36+
uv_mutex_destroy(&id_lock_);
37+
}
38+
// JsObjectMapper interface
39+
objid_t IdForJsObj(const v8::Local<v8::Object> o) override;
40+
v8::Local<v8::Object> JsObjForId(objid_t id) override;
41+
// PhpObjectMapper interface
42+
objid_t IdForPhpObj(zval *o) override;
43+
zval *PhpObjForId(objid_t id TSRMLS_DC) override;
44+
// ObjectMapper interfaces
45+
bool IsValid() override;
46+
// JsMessageChannel interface
47+
void SendToJs(Message *m, MessageFlags flags TSRMLS_DC) const override;
48+
// PhpMessageChannel interface
49+
void SendToPhp(Message *m, MessageFlags flags) const override;
50+
51+
private:
52+
// Callable from both threads:
53+
objid_t NewId();
54+
// Callable from JS thread:
55+
void ClearJsId(objid_t id);
56+
objid_t ClearAllJsIds();
57+
// Callable from PHP thread:
58+
void ClearPhpId(objid_t id TSRMLS_DC);
59+
// Constructor, invoked from JS thread:
60+
explicit AsyncMapperChannel(AsyncMessageWorker *worker)
61+
: worker_(worker), js_obj_to_id_(), php_obj_to_id_(), php_obj_list_(),
62+
// Id #0 is reserved for "invalid object".
63+
next_id_(1) {
64+
uv_mutex_init(&id_lock_);
65+
js_obj_to_id_.Reset(v8::NativeWeakMap::New(v8::Isolate::GetCurrent()));
66+
}
67+
NAN_DISALLOW_ASSIGN_COPY_MOVE(AsyncMapperChannel);
68+
AsyncMessageWorker* worker_;
69+
70+
// Js Object mapping (along with worker's GetFromPersistent/etc)
71+
// Read/writable only from Js thread.
72+
Nan::Persistent<v8::NativeWeakMap> js_obj_to_id_;
73+
74+
// PHP Object mapping
75+
// Read/writable only from PHP thread.
76+
std::unordered_map<zend_object_handle, objid_t> php_obj_to_id_;
77+
std::vector<zval*> php_obj_list_;
78+
79+
// Ids are allocated from both threads, so mutex is required.
80+
uv_mutex_t id_lock_;
81+
objid_t next_id_;
82+
};
83+
84+
} // namespace amw
85+
86+
} // namespace node_php_embed
87+
88+
#endif // NODE_PHP_EMBED_ASYNCMAPPERCHANNEL_H_

0 commit comments

Comments
 (0)