+ psi_object *obj = PSI_OBJ(NULL, o);
+
+ if (obj->data) {
+ if (obj->dtor) {
+ obj->dtor(obj->data);
+ }
+ obj->data = NULL;
+ }
+ zend_object_std_dtor(o);
+}
+
+zend_object *psi_object_init_ex(zend_class_entry *ce, void *data, void (*dtor)(void *))
+{
+ psi_object *o;
+
+ if (!ce) {
+ ce = psi_class_entry;
+ }
+
+ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
+
+ o->data = data;
+ o->dtor = dtor;
+
+ zend_object_std_init(&o->std, ce);
+ object_properties_init(&o->std, ce);
+ o->std.handlers = &psi_object_handlers;
+ return &o->std;
+}
+
+zend_object *psi_object_init(zend_class_entry *ce)
+{
+ return psi_object_init_ex(ce, NULL, NULL);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_psi_dump, 0, 0, 0)
+ ZEND_ARG_INFO(0, stream)
+ZEND_END_ARG_INFO();
+static PHP_FUNCTION(psi_dump)
+{
+ php_stream *s;
+ zval *r = NULL;
+ int fd = STDOUT_FILENO;
+
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &r)) {
+ return;
+ }
+ if (r) {
+ php_stream_from_zval(s, r);
+
+ if (SUCCESS != php_stream_cast(s, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void **)&fd, 1)) {
+ RETURN_FALSE;
+ }
+ }
+ psi_context_dump(PSI_G(context), fd);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate, 0, 0, 1)
+ ZEND_ARG_INFO(0, file)
+ ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+static PHP_FUNCTION(psi_validate)
+{
+ zend_string *file;
+ struct psi_parser_input *I;
+ struct psi_parser P;
+ struct psi_data D = {0};
+ zend_long flags = 0;
+
+#if PHP_DEBUG
+ if (psi_check_env("PSI_DEBUG")) {
+ flags |= PSI_DEBUG;
+ }
+#endif
+
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &file, &flags)) {
+ return;
+ }
+
+ if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
+ RETURN_FALSE;
+ }
+ if (!(I = psi_parser_open_file(&P, file->val, true))) {
+ psi_parser_dtor(&P);
+ RETURN_FALSE;
+ }
+
+ psi_parser_parse(&P, I);
+ psi_data_ctor(&D, P.error, P.flags);
+ RETVAL_BOOL(psi_data_validate(&D, PSI_DATA(&P)) && !P.errors);
+ psi_data_dtor(&D);
+ psi_parser_dtor(&P);
+ free(I);
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate_string, 0, 0, 1)
+ ZEND_ARG_INFO(0, string)
+ZEND_END_ARG_INFO();
+static PHP_FUNCTION(psi_validate_string)
+{
+ zend_string *string;
+ struct psi_parser_input *I;
+ struct psi_parser P;
+ struct psi_data D = {0};
+ zend_long flags = 0;
+
+#if PHP_DEBUG
+ if (psi_check_env("PSI_DEBUG")) {
+ flags |= PSI_DEBUG;
+ }
+#endif
+
+ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &string, &flags)) {
+ return;
+ }
+
+ if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
+ RETURN_FALSE;
+ }
+ if (!(I = psi_parser_open_string(&P, string->val, string->len))) {
+ psi_parser_dtor(&P);
+ RETURN_FALSE;
+ }
+
+ psi_parser_parse(&P, I);
+ psi_data_ctor(&D, P.error, P.flags);
+ RETVAL_BOOL(psi_data_validate(&D, PSI_DATA(&P)) && !P.errors);
+ psi_data_dtor(&D);
+ psi_parser_dtor(&P);
+ free(I);
+}
+
+static PHP_MINIT_FUNCTION(psi)
+{
+ struct psi_context_ops *ops = NULL;
+ zend_class_entry ce = {0};
+ unsigned flags = 0;