1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@thebrainroom.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 /* Infrastructure for working with persistent COM objects.
18 * Implements: IStream* wrapper for PHP streams.
19 * TODO: Magic __wakeup and __sleep handlers for serialization
20 * (can wait till 5.1) */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "php.h"
27 #include "php_ini.h"
28 #include "ext/standard/info.h"
29 #include "php_com_dotnet.h"
30 #include "php_com_dotnet_internal.h"
31 #include "Zend/zend_exceptions.h"
32 #include "com_persist_arginfo.h"
33
34 /* {{{ expose php_stream as a COM IStream */
35
36 typedef struct {
37 CONST_VTBL struct IStreamVtbl *lpVtbl;
38 DWORD engine_thread;
39 LONG refcount;
40 php_stream *stream;
41 zend_resource *res;
42 } php_istream;
43
44 static int le_istream;
45 static void istream_destructor(php_istream *stm);
46
istream_dtor(zend_resource * rsrc)47 static void istream_dtor(zend_resource *rsrc)
48 {
49 php_istream *stm = (php_istream *)rsrc->ptr;
50 istream_destructor(stm);
51 }
52
53 #define FETCH_STM() \
54 php_istream *stm = (php_istream*)This; \
55 if (GetCurrentThreadId() != stm->engine_thread) \
56 return RPC_E_WRONG_THREAD;
57
58 #define FETCH_STM_EX() \
59 php_istream *stm = (php_istream*)This; \
60 if (GetCurrentThreadId() != stm->engine_thread) \
61 return RPC_E_WRONG_THREAD;
62
stm_queryinterface(IStream * This,REFIID riid,void ** ppvObject)63 static HRESULT STDMETHODCALLTYPE stm_queryinterface(
64 IStream *This,
65 /* [in] */ REFIID riid,
66 /* [iid_is][out] */ void **ppvObject)
67 {
68 FETCH_STM_EX();
69
70 if (IsEqualGUID(&IID_IUnknown, riid) ||
71 IsEqualGUID(&IID_IStream, riid)) {
72 *ppvObject = This;
73 InterlockedIncrement(&stm->refcount);
74 return S_OK;
75 }
76
77 *ppvObject = NULL;
78 return E_NOINTERFACE;
79 }
80
stm_addref(IStream * This)81 static ULONG STDMETHODCALLTYPE stm_addref(IStream *This)
82 {
83 FETCH_STM_EX();
84
85 return InterlockedIncrement(&stm->refcount);
86 }
87
stm_release(IStream * This)88 static ULONG STDMETHODCALLTYPE stm_release(IStream *This)
89 {
90 ULONG ret;
91 FETCH_STM();
92
93 ret = InterlockedDecrement(&stm->refcount);
94 if (ret == 0) {
95 /* destroy it */
96 if (stm->res)
97 zend_list_delete(stm->res);
98 }
99 return ret;
100 }
101
stm_read(IStream * This,void * pv,ULONG cb,ULONG * pcbRead)102 static HRESULT STDMETHODCALLTYPE stm_read(IStream *This, void *pv, ULONG cb, ULONG *pcbRead)
103 {
104 ULONG nread;
105 FETCH_STM();
106
107 nread = (ULONG)php_stream_read(stm->stream, pv, cb);
108
109 if (pcbRead) {
110 *pcbRead = nread > 0 ? nread : 0;
111 }
112 if (nread > 0) {
113 return S_OK;
114 }
115 return S_FALSE;
116 }
117
stm_write(IStream * This,void const * pv,ULONG cb,ULONG * pcbWritten)118 static HRESULT STDMETHODCALLTYPE stm_write(IStream *This, void const *pv, ULONG cb, ULONG *pcbWritten)
119 {
120 ssize_t nwrote;
121 FETCH_STM();
122
123 nwrote = php_stream_write(stm->stream, pv, cb);
124
125 if (pcbWritten) {
126 *pcbWritten = nwrote > 0 ? (ULONG)nwrote : 0;
127 }
128 if (nwrote > 0) {
129 return S_OK;
130 }
131 return S_FALSE;
132 }
133
stm_seek(IStream * This,LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER * plibNewPosition)134 static HRESULT STDMETHODCALLTYPE stm_seek(IStream *This, LARGE_INTEGER dlibMove,
135 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
136 {
137 off_t offset;
138 int whence;
139 int ret;
140 FETCH_STM();
141
142 switch (dwOrigin) {
143 case STREAM_SEEK_SET: whence = SEEK_SET; break;
144 case STREAM_SEEK_CUR: whence = SEEK_CUR; break;
145 case STREAM_SEEK_END: whence = SEEK_END; break;
146 default:
147 return STG_E_INVALIDFUNCTION;
148 }
149
150 if (dlibMove.HighPart) {
151 /* we don't support 64-bit offsets */
152 return STG_E_INVALIDFUNCTION;
153 }
154
155 offset = (off_t) dlibMove.QuadPart;
156
157 ret = php_stream_seek(stm->stream, offset, whence);
158
159 if (plibNewPosition) {
160 plibNewPosition->QuadPart = (ULONGLONG)(ret >= 0 ? ret : 0);
161 }
162
163 return ret >= 0 ? S_OK : STG_E_INVALIDFUNCTION;
164 }
165
stm_set_size(IStream * This,ULARGE_INTEGER libNewSize)166 static HRESULT STDMETHODCALLTYPE stm_set_size(IStream *This, ULARGE_INTEGER libNewSize)
167 {
168 FETCH_STM();
169
170 if (libNewSize.HighPart) {
171 return STG_E_INVALIDFUNCTION;
172 }
173
174 if (php_stream_truncate_supported(stm->stream)) {
175 int ret = php_stream_truncate_set_size(stm->stream, (size_t)libNewSize.QuadPart);
176
177 if (ret == 0) {
178 return S_OK;
179 }
180 }
181
182 return STG_E_INVALIDFUNCTION;
183 }
184
stm_copy_to(IStream * This,IStream * pstm,ULARGE_INTEGER cb,ULARGE_INTEGER * pcbRead,ULARGE_INTEGER * pcbWritten)185 static HRESULT STDMETHODCALLTYPE stm_copy_to(IStream *This, IStream *pstm, ULARGE_INTEGER cb,
186 ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
187 {
188 FETCH_STM_EX();
189
190 return E_NOTIMPL;
191 }
192
stm_commit(IStream * This,DWORD grfCommitFlags)193 static HRESULT STDMETHODCALLTYPE stm_commit(IStream *This, DWORD grfCommitFlags)
194 {
195 FETCH_STM();
196
197 php_stream_flush(stm->stream);
198
199 return S_OK;
200 }
201
stm_revert(IStream * This)202 static HRESULT STDMETHODCALLTYPE stm_revert(IStream *This)
203 {
204 /* NOP */
205 return S_OK;
206 }
207
stm_lock_region(IStream * This,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD lockType)208 static HRESULT STDMETHODCALLTYPE stm_lock_region(IStream *This,
209 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
210 {
211 return STG_E_INVALIDFUNCTION;
212 }
213
stm_unlock_region(IStream * This,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD lockType)214 static HRESULT STDMETHODCALLTYPE stm_unlock_region(IStream *This,
215 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD lockType)
216 {
217 return STG_E_INVALIDFUNCTION;
218 }
219
stm_stat(IStream * This,STATSTG * pstatstg,DWORD grfStatFlag)220 static HRESULT STDMETHODCALLTYPE stm_stat(IStream *This,
221 STATSTG *pstatstg, DWORD grfStatFlag)
222 {
223 return STG_E_INVALIDFUNCTION;
224 }
225
stm_clone(IStream * This,IStream ** ppstm)226 static HRESULT STDMETHODCALLTYPE stm_clone(IStream *This, IStream **ppstm)
227 {
228 return STG_E_INVALIDFUNCTION;
229 }
230
231 static struct IStreamVtbl php_istream_vtbl = {
232 stm_queryinterface,
233 stm_addref,
234 stm_release,
235 stm_read,
236 stm_write,
237 stm_seek,
238 stm_set_size,
239 stm_copy_to,
240 stm_commit,
241 stm_revert,
242 stm_lock_region,
243 stm_unlock_region,
244 stm_stat,
245 stm_clone
246 };
247
istream_destructor(php_istream * stm)248 static void istream_destructor(php_istream *stm)
249 {
250 if (stm->refcount > 0) {
251 CoDisconnectObject((IUnknown*)stm, 0);
252 }
253
254 zend_list_delete(stm->stream->res);
255
256 CoTaskMemFree(stm);
257 }
258 /* }}} */
259
php_com_wrapper_export_stream(php_stream * stream)260 PHP_COM_DOTNET_API IStream *php_com_wrapper_export_stream(php_stream *stream)
261 {
262 php_istream *stm = (php_istream*)CoTaskMemAlloc(sizeof(*stm));
263
264 if (stm == NULL)
265 return NULL;
266
267 memset(stm, 0, sizeof(*stm));
268 stm->engine_thread = GetCurrentThreadId();
269 stm->lpVtbl = &php_istream_vtbl;
270 stm->refcount = 1;
271 stm->stream = stream;
272
273 GC_ADDREF(stream->res);
274 stm->res = zend_register_resource(stm, le_istream);
275
276 return (IStream*)stm;
277 }
278
279 #define CPH_METHOD(fname) PHP_METHOD(COMPersistHelper, fname)
280
281 #define CPH_FETCH() php_com_persist_helper *helper = (php_com_persist_helper*)Z_OBJ_P(getThis());
282
283 #define CPH_NO_OBJ() if (helper->unk == NULL) { php_com_throw_exception(E_INVALIDARG, "No COM object is associated with this helper instance"); RETURN_THROWS(); }
284
285 typedef struct {
286 zend_object std;
287 long codepage;
288 IUnknown *unk;
289 IPersistStream *ips;
290 IPersistStreamInit *ipsi;
291 IPersistFile *ipf;
292 } php_com_persist_helper;
293
294 static zend_object_handlers helper_handlers;
295 static zend_class_entry *helper_ce;
296
get_persist_stream(php_com_persist_helper * helper)297 static inline HRESULT get_persist_stream(php_com_persist_helper *helper)
298 {
299 if (!helper->ips && helper->unk) {
300 return IUnknown_QueryInterface(helper->unk, &IID_IPersistStream, &helper->ips);
301 }
302 return helper->ips ? S_OK : E_NOTIMPL;
303 }
304
get_persist_stream_init(php_com_persist_helper * helper)305 static inline HRESULT get_persist_stream_init(php_com_persist_helper *helper)
306 {
307 if (!helper->ipsi && helper->unk) {
308 return IUnknown_QueryInterface(helper->unk, &IID_IPersistStreamInit, &helper->ipsi);
309 }
310 return helper->ipsi ? S_OK : E_NOTIMPL;
311 }
312
get_persist_file(php_com_persist_helper * helper)313 static inline HRESULT get_persist_file(php_com_persist_helper *helper)
314 {
315 if (!helper->ipf && helper->unk) {
316 return IUnknown_QueryInterface(helper->unk, &IID_IPersistFile, &helper->ipf);
317 }
318 return helper->ipf ? S_OK : E_NOTIMPL;
319 }
320
321
322 /* {{{ Determines the filename into which an object will be saved, or false if none is set, via IPersistFile::GetCurFile */
CPH_METHOD(GetCurFileName)323 CPH_METHOD(GetCurFileName)
324 {
325 HRESULT res;
326 OLECHAR *olename = NULL;
327 CPH_FETCH();
328
329 if (zend_parse_parameters_none() == FAILURE) {
330 RETURN_THROWS();
331 }
332
333 CPH_NO_OBJ();
334
335 res = get_persist_file(helper);
336 if (helper->ipf) {
337 res = IPersistFile_GetCurFile(helper->ipf, &olename);
338
339 if (res == S_OK) {
340 size_t len;
341 char *str = php_com_olestring_to_string(olename,
342 &len, helper->codepage);
343 RETVAL_STRINGL(str, len);
344 // TODO: avoid reallocarion???
345 efree(str);
346 CoTaskMemFree(olename);
347 return;
348 } else if (res == S_FALSE) {
349 CoTaskMemFree(olename);
350 RETURN_FALSE;
351 }
352 php_com_throw_exception(res, NULL);
353 } else {
354 php_com_throw_exception(res, NULL);
355 }
356 }
357 /* }}} */
358
359
360 /* {{{ Persist object data to file, via IPersistFile::Save */
CPH_METHOD(SaveToFile)361 CPH_METHOD(SaveToFile)
362 {
363 HRESULT res;
364 char *filename, *fullpath = NULL;
365 size_t filename_len;
366 bool remember = TRUE;
367 OLECHAR *olefilename = NULL;
368 CPH_FETCH();
369
370 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p!|b",
371 &filename, &filename_len, &remember)) {
372 RETURN_THROWS();
373 }
374
375 CPH_NO_OBJ();
376
377 res = get_persist_file(helper);
378 if (helper->ipf) {
379 if (filename) {
380 fullpath = expand_filepath(filename, NULL);
381 if (!fullpath) {
382 RETURN_FALSE;
383 }
384
385 if (php_check_open_basedir(fullpath)) {
386 efree(fullpath);
387 RETURN_FALSE;
388 }
389
390 olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
391 efree(fullpath);
392 }
393 res = IPersistFile_Save(helper->ipf, olefilename, remember);
394 if (SUCCEEDED(res)) {
395 if (!olefilename) {
396 res = IPersistFile_GetCurFile(helper->ipf, &olefilename);
397 if (S_OK == res) {
398 IPersistFile_SaveCompleted(helper->ipf, olefilename);
399 CoTaskMemFree(olefilename);
400 olefilename = NULL;
401 }
402 } else if (remember) {
403 IPersistFile_SaveCompleted(helper->ipf, olefilename);
404 }
405 }
406
407 if (olefilename) {
408 efree(olefilename);
409 }
410
411 if (FAILED(res)) {
412 php_com_throw_exception(res, NULL);
413 }
414
415 } else {
416 php_com_throw_exception(res, NULL);
417 }
418 }
419 /* }}} */
420
421 /* {{{ Load object data from file, via IPersistFile::Load */
CPH_METHOD(LoadFromFile)422 CPH_METHOD(LoadFromFile)
423 {
424 HRESULT res;
425 char *filename, *fullpath;
426 size_t filename_len;
427 zend_long flags = 0;
428 OLECHAR *olefilename;
429 CPH_FETCH();
430
431 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "p|l",
432 &filename, &filename_len, &flags)) {
433 RETURN_THROWS();
434 }
435
436 CPH_NO_OBJ();
437
438 res = get_persist_file(helper);
439 if (helper->ipf) {
440 if (!(fullpath = expand_filepath(filename, NULL))) {
441 RETURN_FALSE;
442 }
443
444 if (php_check_open_basedir(fullpath)) {
445 efree(fullpath);
446 RETURN_FALSE;
447 }
448
449 olefilename = php_com_string_to_olestring(fullpath, strlen(fullpath), helper->codepage);
450 efree(fullpath);
451
452 res = IPersistFile_Load(helper->ipf, olefilename, (DWORD)flags);
453 efree(olefilename);
454
455 if (FAILED(res)) {
456 php_com_throw_exception(res, NULL);
457 }
458
459 } else {
460 php_com_throw_exception(res, NULL);
461 }
462 }
463 /* }}} */
464
465 /* {{{ Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax) */
CPH_METHOD(GetMaxStreamSize)466 CPH_METHOD(GetMaxStreamSize)
467 {
468 HRESULT res;
469 ULARGE_INTEGER size;
470 CPH_FETCH();
471
472 if (zend_parse_parameters_none() == FAILURE) {
473 RETURN_THROWS();
474 }
475
476 CPH_NO_OBJ();
477
478 res = get_persist_stream_init(helper);
479 if (helper->ipsi) {
480 res = IPersistStreamInit_GetSizeMax(helper->ipsi, &size);
481 } else {
482 res = get_persist_stream(helper);
483 if (helper->ips) {
484 res = IPersistStream_GetSizeMax(helper->ips, &size);
485 } else {
486 php_com_throw_exception(res, NULL);
487 RETURN_THROWS();
488 }
489 }
490
491 if (res != S_OK) {
492 php_com_throw_exception(res, NULL);
493 } else {
494 /* TODO: handle 64 bit properly */
495 RETURN_LONG((zend_long)size.QuadPart);
496 }
497 }
498 /* }}} */
499
500 /* {{{ Initializes the object to a default state, via IPersistStreamInit::InitNew */
CPH_METHOD(InitNew)501 CPH_METHOD(InitNew)
502 {
503 HRESULT res;
504 CPH_FETCH();
505
506 if (zend_parse_parameters_none() == FAILURE) {
507 RETURN_THROWS();
508 }
509
510 CPH_NO_OBJ();
511
512 res = get_persist_stream_init(helper);
513 if (helper->ipsi) {
514 res = IPersistStreamInit_InitNew(helper->ipsi);
515
516 if (res != S_OK) {
517 php_com_throw_exception(res, NULL);
518 } else {
519 RETURN_TRUE;
520 }
521 } else {
522 php_com_throw_exception(res, NULL);
523 }
524 }
525 /* }}} */
526
527 /* {{{ Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream */
CPH_METHOD(LoadFromStream)528 CPH_METHOD(LoadFromStream)
529 {
530 zval *zstm;
531 php_stream *stream;
532 IStream *stm = NULL;
533 HRESULT res;
534 CPH_FETCH();
535
536 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
537 RETURN_THROWS();
538 }
539
540 php_stream_from_zval_no_verify(stream, zstm);
541
542 if (stream == NULL) {
543 php_com_throw_exception(E_INVALIDARG, "expected a stream");
544 RETURN_THROWS();
545 }
546
547 stm = php_com_wrapper_export_stream(stream);
548 if (stm == NULL) {
549 php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
550 RETURN_THROWS();
551 }
552
553 res = S_OK;
554 RETVAL_TRUE;
555
556 if (helper->unk == NULL) {
557 IDispatch *disp = NULL;
558
559 /* we need to create an object and load using OleLoadFromStream */
560 res = OleLoadFromStream(stm, &IID_IDispatch, &disp);
561
562 if (SUCCEEDED(res)) {
563 php_com_wrap_dispatch(return_value, disp, COMG(code_page));
564 }
565 } else {
566 res = get_persist_stream_init(helper);
567 if (helper->ipsi) {
568 res = IPersistStreamInit_Load(helper->ipsi, stm);
569 } else {
570 res = get_persist_stream(helper);
571 if (helper->ips) {
572 res = IPersistStreamInit_Load(helper->ipsi, stm);
573 }
574 }
575 }
576 IStream_Release(stm);
577
578 if (FAILED(res)) {
579 php_com_throw_exception(res, NULL);
580 RETURN_THROWS();
581 }
582 }
583 /* }}} */
584
585 /* {{{ Saves the object to a stream, via IPersistStream::Save */
CPH_METHOD(SaveToStream)586 CPH_METHOD(SaveToStream)
587 {
588 zval *zstm;
589 php_stream *stream;
590 IStream *stm = NULL;
591 HRESULT res;
592 CPH_FETCH();
593
594 CPH_NO_OBJ();
595
596 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstm)) {
597 RETURN_THROWS();
598 }
599
600 php_stream_from_zval_no_verify(stream, zstm);
601
602 if (stream == NULL) {
603 php_com_throw_exception(E_INVALIDARG, "expected a stream");
604 RETURN_THROWS();
605 }
606
607 stm = php_com_wrapper_export_stream(stream);
608 if (stm == NULL) {
609 php_com_throw_exception(E_UNEXPECTED, "failed to wrap stream");
610 RETURN_THROWS();
611 }
612
613 res = get_persist_stream_init(helper);
614 if (helper->ipsi) {
615 res = IPersistStreamInit_Save(helper->ipsi, stm, TRUE);
616 } else {
617 res = get_persist_stream(helper);
618 if (helper->ips) {
619 res = IPersistStream_Save(helper->ips, stm, TRUE);
620 }
621 }
622
623 IStream_Release(stm);
624
625 if (FAILED(res)) {
626 php_com_throw_exception(res, NULL);
627 RETURN_THROWS();
628 }
629
630 RETURN_TRUE;
631 }
632 /* }}} */
633
634 /* {{{ Creates a persistence helper object, usually associated with a com_object */
CPH_METHOD(__construct)635 CPH_METHOD(__construct)
636 {
637 php_com_dotnet_object *obj = NULL;
638 zval *zobj = NULL;
639 CPH_FETCH();
640
641 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!",
642 &zobj, php_com_variant_class_entry)) {
643 RETURN_THROWS();
644 }
645
646 if (!zobj) {
647 return;
648 }
649
650 obj = CDNO_FETCH(zobj);
651
652 if (V_VT(&obj->v) != VT_DISPATCH || V_DISPATCH(&obj->v) == NULL) {
653 php_com_throw_exception(E_INVALIDARG, "parameter must represent an IDispatch COM object");
654 RETURN_THROWS();
655 }
656
657 /* it is always safe to cast an interface to IUnknown */
658 helper->unk = (IUnknown*)V_DISPATCH(&obj->v);
659 IUnknown_AddRef(helper->unk);
660 helper->codepage = obj->code_page;
661 }
662 /* }}} */
663
664
helper_free_storage(zend_object * obj)665 static void helper_free_storage(zend_object *obj)
666 {
667 php_com_persist_helper *object = (php_com_persist_helper*)obj;
668
669 if (object->ipf) {
670 IPersistFile_Release(object->ipf);
671 }
672 if (object->ips) {
673 IPersistStream_Release(object->ips);
674 }
675 if (object->ipsi) {
676 IPersistStreamInit_Release(object->ipsi);
677 }
678 if (object->unk) {
679 IUnknown_Release(object->unk);
680 }
681 zend_object_std_dtor(&object->std);
682 }
683
684
helper_clone(zend_object * obj)685 static zend_object* helper_clone(zend_object *obj)
686 {
687 php_com_persist_helper *clone, *object = (php_com_persist_helper*) obj;
688
689 clone = emalloc(sizeof(*object));
690 memcpy(clone, object, sizeof(*object));
691
692 zend_object_std_init(&clone->std, object->std.ce);
693
694 if (clone->ipf) {
695 IPersistFile_AddRef(clone->ipf);
696 }
697 if (clone->ips) {
698 IPersistStream_AddRef(clone->ips);
699 }
700 if (clone->ipsi) {
701 IPersistStreamInit_AddRef(clone->ipsi);
702 }
703 if (clone->unk) {
704 IUnknown_AddRef(clone->unk);
705 }
706 return (zend_object*)clone;
707 }
708
helper_new(zend_class_entry * ce)709 static zend_object* helper_new(zend_class_entry *ce)
710 {
711 php_com_persist_helper *helper;
712
713 helper = emalloc(sizeof(*helper));
714 memset(helper, 0, sizeof(*helper));
715
716 zend_object_std_init(&helper->std, helper_ce);
717 helper->std.handlers = &helper_handlers;
718
719 return &helper->std;
720 }
721
php_com_persist_minit(INIT_FUNC_ARGS)722 int php_com_persist_minit(INIT_FUNC_ARGS)
723 {
724 memcpy(&helper_handlers, &std_object_handlers, sizeof(helper_handlers));
725 helper_handlers.free_obj = helper_free_storage;
726 helper_handlers.clone_obj = helper_clone;
727
728 helper_ce = register_class_COMPersistHelper();
729 helper_ce->create_object = helper_new;
730
731 le_istream = zend_register_list_destructors_ex(istream_dtor,
732 NULL, "com_dotnet_istream_wrapper", module_number);
733
734 return SUCCESS;
735 }
736