From f4a6ca8c7710a9224ca982c53c8756160e90159c Mon Sep 17 00:00:00 2001
From: Metztli Information Technology <jose@metztli.it>
Date: Tue, 15 Oct 2019 06:25:18 -0700
Subject: [PATCH] Enhancing Debian packaging for php7.3_7.3.10-1 to generate
 ZTS and pthreads versions for CLI

---
 changelog                                  |     2 +-
 control                                    |   169 +-
 copyright                                  |    67 +
 patches/metztli/metztli-ext-pthreads.patch | 18954 +++++++++++++++++++
 patches/series                             |     1 +
 rules                                      |     1 +
 rules.d/pthreads.mk                        |    10 +
 7 files changed, 19130 insertions(+), 74 deletions(-)
 create mode 100644 patches/metztli/metztli-ext-pthreads.patch
 create mode 100644 rules.d/pthreads.mk

diff --git a/changelog b/changelog
index f52e44b..e0a8075 100644
--- a/changelog
+++ b/changelog
@@ -1,4 +1,4 @@
-php7.3 (7.3.10-1) unstable; urgency=medium
+php7.3-zts (7.3.10-1) unstable; urgency=medium
 
   * New upstream version 7.3.10
 
diff --git a/control b/control
index 1b5be85..b6f187f 100644
--- a/control
+++ b/control
@@ -1,4 +1,4 @@
-Source: php7.3
+Source: php7.3-zts
 Section: php
 Priority: optional
 Maintainer: Debian PHP Maintainers <team+pkg-php@tracker.debian.org>
@@ -80,15 +80,15 @@ Vcs-Git: https://salsa.debian.org/php-team/php.git -b master-7.3
 Vcs-Browser: https://salsa.debian.org/php-team/php
 Homepage: http://www.php.net/
 
-Package: libapache2-mod-php7.3
+Package: libapache2-mod-php7.3-zts
 Section: httpd
 Architecture: any
 Depends: libmagic1,
          mime-support,
-         php7.3-cli,
-         php7.3-common (= ${binary:Version}),
-         php7.3-json,
-         php7.3-opcache,
+         php7.3-zts-cli,
+         php7.3-zts-common (= ${binary:Version}),
+         php7.3-zts-json,
+         php7.3-zts-opcache,
          tzdata,
          ucf,
          ${misc:Depends},
@@ -108,14 +108,14 @@ Description: server-side, HTML-embedded scripting language (Apache 2 module)
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: libphp7.3-embed
+Package: libphp7.3-zts-embed
 Architecture: any
 Depends: libmagic1,
          mime-support,
-         php7.3-cli,
-         php7.3-common (= ${binary:Version}),
-         php7.3-json,
-         php7.3-opcache,
+         php7.3-zts-cli,
+         php7.3-zts-common (= ${binary:Version}),
+         php7.3-zts-json,
+         php7.3-zts-opcache,
          tzdata,
          ucf,
          ${misc:Depends},
@@ -124,7 +124,7 @@ Provides: libphp-embed,
           ${php:Provides}
 Suggests: php-pear
 Description: HTML-embedded scripting language (Embedded SAPI library)
- This package provides the library /usr/lib/libphp7.3.so which can
+ This package provides the library /usr/lib/libphp7.3-zts.so which can
  be used by application developers to embed PHP scripting functionality.
  .
  ${php:Extensions}
@@ -137,10 +137,10 @@ Description: HTML-embedded scripting language (Embedded SAPI library)
  the API/ABI will be kept compatible even between minor releases.  You
  have been warned.
 
-Package: php7.3
+Package: php7.3-zts
 Architecture: all
-Depends: libapache2-mod-php7.3 | php7.3-fpm | php7.3-cgi,
-         php7.3-common,
+Depends: libapache2-mod-php7.3-zts | php7.3-zts-fpm | php7.3-zts-cgi,
+         php7.3-zts-common,
          ${misc:Depends}
 Provides: php
 Description: server-side, HTML-embedded scripting language (metapackage)
@@ -153,14 +153,14 @@ Description: server-side, HTML-embedded scripting language (metapackage)
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-cgi
+Package: php7.3-zts-cgi
 Architecture: any
 Depends: libmagic1,
          mime-support,
-         php7.3-cli,
-         php7.3-common (= ${binary:Version}),
-         php7.3-json,
-         php7.3-opcache,
+         php7.3-zts-cli,
+         php7.3-zts-common (= ${binary:Version}),
+         php7.3-zts-json,
+         php7.3-zts-opcache,
          tzdata,
          ucf,
          ${misc:Depends},
@@ -172,7 +172,7 @@ Description: server-side, HTML-embedded scripting language (CGI binary)
  This package provides the /usr/lib/cgi-bin/php CGI interpreter built
  for use in Apache 2 with mod_actions, or any other CGI httpd that
  supports a similar mechanism.  Note that MOST users probably
- want the php7.3-fpm package.
+ want the php7.3-zts-fpm package.
  .
  ${php:Extensions}
  .
@@ -180,15 +180,15 @@ Description: server-side, HTML-embedded scripting language (CGI binary)
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-cli
+Package: php7.3-zts-cli
 Architecture: any
 Depends: libedit2 (>= 2.11-20080614-4),
          libmagic1,
          mime-support,
-         php7.3-common (= ${binary:Version}),
-         php7.3-json,
-         php7.3-opcache,
-         php7.3-readline,
+         php7.3-zts-common (= ${binary:Version}),
+         php7.3-zts-json,
+         php7.3-zts-opcache,
+         php7.3-zts-readline,
          tzdata,
          ucf,
          ${misc:Depends},
@@ -197,7 +197,7 @@ Provides: php-cli,
           ${php:Provides}
 Suggests: php-pear
 Description: command-line interpreter for the PHP scripting language
- This package provides the /usr/bin/php7.3 command interpreter, useful for
+ This package provides the /usr/bin/php7.3-zts command interpreter, useful for
  testing PHP scripts from a shell or performing general shell scripting tasks.
  .
  ${php:Extensions}
@@ -206,13 +206,13 @@ Description: command-line interpreter for the PHP scripting language
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-dev
+Package: php7.3-zts-dev
 Depends: autoconf (>= 2.63),
          automake (>= 1.11),
 	 libpcre2-dev (>= 10.30),
          libssl-dev,
-         php7.3-cli (>= ${binary:Version}),
-         php7.3-common (= ${binary:Version}),
+         php7.3-zts-cli (>= ${binary:Version}),
+         php7.3-zts-common (= ${binary:Version}),
          shtool,
          ${libtool:Depends},
          ${misc:Depends},
@@ -229,14 +229,14 @@ Description: Files for PHP7.3 module development
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-fpm
+Package: php7.3-zts-fpm
 Architecture: any
 Depends: libmagic1,
          mime-support,
-         php7.3-cli,
-         php7.3-common (= ${binary:Version}),
-         php7.3-json,
-         php7.3-opcache,
+         php7.3-zts-cli,
+         php7.3-zts-common (= ${binary:Version}),
+         php7.3-zts-json,
+         php7.3-zts-opcache,
          tzdata,
          ucf,
          ${misc:Depends},
@@ -248,7 +248,7 @@ Pre-Depends: ${misc:Pre-Depends}
 Description: server-side, HTML-embedded scripting language (FPM-CGI binary)
  This package provides the Fast Process Manager interpreter that runs
  as a daemon and receives Fast/CGI requests.  Note that MOST Apache users
- probably want the libapache2-mod-php7.3 package.
+ probably want the libapache2-mod-php7.3-zts package.
  .
  ${php:Extensions}
  .
@@ -256,14 +256,14 @@ Description: server-side, HTML-embedded scripting language (FPM-CGI binary)
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-phpdbg
+Package: php7.3-zts-phpdbg
 Architecture: any
 Depends: libmagic1,
          mime-support,
-         php7.3-cli,
-         php7.3-common (= ${binary:Version}),
-         php7.3-json,
-         php7.3-opcache,
+         php7.3-zts-cli,
+         php7.3-zts-common (= ${binary:Version}),
+         php7.3-zts-json,
+         php7.3-zts-opcache,
          tzdata,
          ucf,
          ${misc:Depends},
@@ -281,16 +281,16 @@ Description: server-side, HTML-embedded scripting language (PHPDBG binary)
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-xsl
+Package: php7.3-zts-xsl
 Architecture: all
-Depends: php7.3-common,
-         php7.3-xml,
+Depends: php7.3-zts-common,
+         php7.3-zts-xml,
          ${misc:Depends}
 Description: XSL module for PHP (dummy)
  This package is a transitional dummy package and can be safely
  removed.
 
-Package: php7.3-odbc
+Package: php7.3-zts-odbc
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -313,7 +313,30 @@ Description: ODBC module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-readline
+Package: php7.3-zts-pthreads
+Architecture: any
+Depends: php-common (>= 1:35),
+         ucf,
+         ${misc:Depends},
+         ${php:Depends},
+         ${php-pthreads:Depends},
+         ${shlibs:Depends}
+Pre-Depends: ${misc:Pre-Depends},
+             ${php-pthreads:Pre-Depends}
+Built-Using: ${php:Built-Using}
+Replaces: ${php-pthreads:Replaces}
+Breaks: ${php-pthreads:Breaks}
+Conflicts: ${php-pthreads:Conflicts}
+Provides: ${php-pthreads:Provides}
+Recommends: ${php-pthreads:Recommends}
+Description: KrakJoe pthreads module for PHP
+ This package provides the KrakJoe pthreads module(s) for PHP.
+ .
+ PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely-used
+ open source general-purpose scripting language that is especially suited
+ for web development and can be embedded into HTML.
+
+Package: php7.3-zts-readline
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -336,7 +359,7 @@ Description: readline module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-recode
+Package: php7.3-zts-recode
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -359,7 +382,7 @@ Description: recode module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-sqlite3
+Package: php7.3-zts-sqlite3
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -382,7 +405,7 @@ Description: SQLite3 module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-tidy
+Package: php7.3-zts-tidy
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -405,7 +428,7 @@ Description: tidy module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-xml
+Package: php7.3-zts-xml
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -428,7 +451,7 @@ Description: DOM, SimpleXML, WDDX, XML, and XSL module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-sybase
+Package: php7.3-zts-sybase
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -451,7 +474,7 @@ Description: Sybase module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-gd
+Package: php7.3-zts-gd
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -474,7 +497,7 @@ Description: GD module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-gmp
+Package: php7.3-zts-gmp
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -497,7 +520,7 @@ Description: GMP module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-zip
+Package: php7.3-zts-zip
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -520,7 +543,7 @@ Description: Zip module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-common
+Package: php7.3-zts-common
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -543,7 +566,7 @@ Description: documentation, examples and common module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-intl
+Package: php7.3-zts-intl
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -566,7 +589,7 @@ Description: Internationalisation module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-snmp
+Package: php7.3-zts-snmp
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -589,7 +612,7 @@ Description: SNMP module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-curl
+Package: php7.3-zts-curl
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -612,7 +635,7 @@ Description: CURL module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-json
+Package: php7.3-zts-json
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -635,7 +658,7 @@ Description: JSON module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-pgsql
+Package: php7.3-zts-pgsql
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -658,7 +681,7 @@ Description: PostgreSQL module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-mbstring
+Package: php7.3-zts-mbstring
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -681,7 +704,7 @@ Description: MBSTRING module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-enchant
+Package: php7.3-zts-enchant
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -704,7 +727,7 @@ Description: Enchant module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-opcache
+Package: php7.3-zts-opcache
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -727,7 +750,7 @@ Description: Zend OpCache module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-imap
+Package: php7.3-zts-imap
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -750,7 +773,7 @@ Description: IMAP module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-mysql
+Package: php7.3-zts-mysql
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -773,7 +796,7 @@ Description: MySQL module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-bcmath
+Package: php7.3-zts-bcmath
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -796,7 +819,7 @@ Description: Bcmath module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-soap
+Package: php7.3-zts-soap
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -819,7 +842,7 @@ Description: SOAP module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-dba
+Package: php7.3-zts-dba
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -842,7 +865,7 @@ Description: DBA module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-interbase
+Package: php7.3-zts-interbase
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -865,7 +888,7 @@ Description: Interbase module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-xmlrpc
+Package: php7.3-zts-xmlrpc
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -888,7 +911,7 @@ Description: XMLRPC-EPI module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-pspell
+Package: php7.3-zts-pspell
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -911,7 +934,7 @@ Description: pspell module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-bz2
+Package: php7.3-zts-bz2
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
@@ -934,7 +957,7 @@ Description: bzip2 module for PHP
  open source general-purpose scripting language that is especially suited
  for web development and can be embedded into HTML.
 
-Package: php7.3-ldap
+Package: php7.3-zts-ldap
 Architecture: any
 Depends: php-common (>= 1:35),
          ucf,
diff --git a/copyright b/copyright
index 775b6a7..1df0c33 100644
--- a/copyright
+++ b/copyright
@@ -738,3 +738,70 @@ License: BSD-4-clause
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  SUCH DAMAGE.
+
+License: pthreads
+-------------------------------------------------------------------- 
+                  The PHP License, version 3.01
+Copyright (c) 2012-2014 Joe Watkins. All rights reserved.
+-------------------------------------------------------------------- 
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+ 
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+ 
+  3. The name "PHP" must not be used to endorse or promote products
+     derived from this software without prior written permission. For
+     written permission, please contact group@php.net.
+  
+  4. Products derived from this software may not be called "PHP", nor
+     may "PHP" appear in their name, without prior written permission
+     from group@php.net.  You may indicate that your software works in
+     conjunction with PHP by saying "Foo for PHP" instead of calling
+     it "PHP Foo" or "phpfoo"
+ 
+  5. The PHP Group may publish revised and/or new versions of the
+     license from time to time. Each version will be given a
+     distinguishing version number.
+     Once covered code has been published under a particular version
+     of the license, you may always continue to use it under the terms
+     of that version. You may also choose to use such covered code
+     under the terms of any subsequent version of the license
+     published by the PHP Group. No one other than the PHP Group has
+     the right to modify the terms applicable to covered code created
+     under this License.
+
+  6. Redistributions of any form whatsoever must retain the following
+     acknowledgment:
+     "This product includes PHP software, freely available from
+     <http://www.php.net/software/>".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PHP
+DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------- 
+
+The PHP Group can be contacted via Email at group@php.net.
+
+For more information on the PHP Group and the PHP project, 
+please see <http://www.php.net>.
+
+PHP includes the Zend Engine, freely available at
+<http://www.zend.com>.
diff --git a/patches/metztli/metztli-ext-pthreads.patch b/patches/metztli/metztli-ext-pthreads.patch
new file mode 100644
index 0000000..228d5b1
--- /dev/null
+++ b/patches/metztli/metztli-ext-pthreads.patch
@@ -0,0 +1,18954 @@
+From ad25b7d759b767a65d90c27531ea9885204690eb Mon Sep 17 00:00:00 2001
+From: Metztli Information Technology <jose@metztli.it>
+Date: Mon, 14 Oct 2019 05:01:54 -0700
+Subject: [PATCH] Ome commit which added krakjoe phtreads into PHP 7.3.10 ext
+ directory
+
+---
+ ext/pthreads/.github/ISSUE_TEMPLATE.md        |   34 +
+ ext/pthreads/.gitignore                       |    1 +
+ ext/pthreads/LICENSE                          |   65 +
+ ext/pthreads/README.md                        |  199 +++
+ ext/pthreads/appveyor.yml                     |   91 ++
+ ext/pthreads/classes/collectable.h            |   32 +
+ ext/pthreads/classes/pool.h                   |  349 ++++++
+ ext/pthreads/classes/socket.h                 |  411 +++++++
+ ext/pthreads/classes/thread.h                 |  155 +++
+ ext/pthreads/classes/threaded.h               |  333 +++++
+ ext/pthreads/classes/worker.h                 |  259 ++++
+ ext/pthreads/config.m4                        |   37 +
+ ext/pthreads/config.w32                       |   25 +
+ ext/pthreads/examples/Benchmark.php           |   35 +
+ ext/pthreads/examples/CallAnyFunction.php     |   73 ++
+ ext/pthreads/examples/ChannelsAndMagic.php    |   57 +
+ ext/pthreads/examples/ClosureFuture.php       |   60 +
+ ext/pthreads/examples/KeepAliveSession.php    |  194 +++
+ ext/pthreads/examples/MySQLi.php              |   82 ++
+ .../examples/NonBlockingSocketServer.php      |  129 ++
+ ext/pthreads/examples/ObjectsAsParameters.php |  101 ++
+ ext/pthreads/examples/Pooling.php             |  115 ++
+ .../examples/SelectiveInheritance.php         |   72 ++
+ ext/pthreads/examples/SharingResources.php    |   68 +
+ ext/pthreads/examples/SimpleWebRequest.php    |   39 +
+ ext/pthreads/examples/Singleton.php           |   58 +
+ ext/pthreads/examples/SocketServer.php        |   81 ++
+ ext/pthreads/examples/Sockets.php             |   34 +
+ ext/pthreads/examples/StackableArray.php      |   65 +
+ ext/pthreads/examples/Synchronization.php     |   36 +
+ .../examples/fetching-data-from-a-thread.php  |   38 +
+ ext/pthreads/examples/stub.php                |  698 +++++++++++
+ ext/pthreads/package.xml                      |  286 +++++
+ ext/pthreads/php_pthreads.c                   |  898 ++++++++++++++
+ ext/pthreads/php_pthreads.h                   |   57 +
+ ext/pthreads/src/copy.h                       |  410 +++++++
+ ext/pthreads/src/globals.c                    |  170 +++
+ ext/pthreads/src/globals.h                    |   97 ++
+ ext/pthreads/src/handlers.c                   |  380 ++++++
+ ext/pthreads/src/handlers.h                   |  121 ++
+ ext/pthreads/src/monitor.c                    |  132 ++
+ ext/pthreads/src/monitor.h                    |   43 +
+ ext/pthreads/src/object.c                     |  536 ++++++++
+ ext/pthreads/src/object.h                     |   60 +
+ ext/pthreads/src/prepare.c                    |  894 ++++++++++++++
+ ext/pthreads/src/prepare.h                    |   45 +
+ ext/pthreads/src/pthreads.h                   |  194 +++
+ ext/pthreads/src/resources.c                  |   56 +
+ ext/pthreads/src/resources.h                  |   33 +
+ ext/pthreads/src/socket.c                     | 1066 ++++++++++++++++
+ ext/pthreads/src/socket.h                     |   60 +
+ ext/pthreads/src/sockets/windows_common.h     |  117 ++
+ ext/pthreads/src/stack.c                      |  291 +++++
+ ext/pthreads/src/stack.h                      |   40 +
+ ext/pthreads/src/store.c                      | 1089 +++++++++++++++++
+ ext/pthreads/src/store.h                      |   67 +
+ ext/pthreads/src/thread.h                     |  131 ++
+ ext/pthreads/tests/anon-bound-inherited.phpt  |   34 +
+ ext/pthreads/tests/anon-interceptors.phpt     |   57 +
+ .../tests/anon-unbound-double-inherited.phpt  |   44 +
+ .../tests/anon-unbound-inherited.phpt         |   35 +
+ .../tests/anon-unbound-use-guards.phpt        |   24 +
+ ext/pthreads/tests/binding.phpt               |   55 +
+ ext/pthreads/tests/callstatic-etc.phpt        |   28 +
+ .../tests/child-to-parent-class-copying.phpt  |   33 +
+ ext/pthreads/tests/chunk.phpt                 |   40 +
+ ext/pthreads/tests/class-defaults.phpt        |   51 +
+ .../tests/class-static-properties-2.phpt      |   24 +
+ ...-static-properties-double-inheritance.phpt |   32 +
+ ...erties-order-dependencies-inheritance.phpt |   29 +
+ ...-static-properties-order-dependencies.phpt |   29 +
+ .../tests/class-static-properties.phpt        |  120 ++
+ .../tests/closure-delayed-include.phpt        |   49 +
+ .../tests/closure-secondary-thread-copy.phpt  |   31 +
+ ...ures-as-members-repeated-reallocation.phpt |   26 +
+ ext/pthreads/tests/closures-as-members.phpt   |   67 +
+ .../tests/complex-statics-set-null.phpt       |   64 +
+ ext/pthreads/tests/complex-statics.phpt       |   46 +
+ ext/pthreads/tests/constant-array.phpt        |   17 +
+ .../tests/constant-ast-copy-missing-type.phpt |   31 +
+ ext/pthreads/tests/constant-ast-copy.phpt     |   30 +
+ ext/pthreads/tests/constants.phpt             |   15 +
+ ext/pthreads/tests/corrupted-resources.phpt   |   39 +
+ ext/pthreads/tests/counting-members.phpt      |   19 +
+ .../tests/crazy-refcounting-stuff.phpt        |   56 +
+ .../tests/display-startup-errors-output.phpt  |   43 +
+ ext/pthreads/tests/doc-comments.phpt          |   68 +
+ .../exception-handler-caught-nested.phpt      |   74 ++
+ .../exception-handler-caught-uncaught.phpt    |   89 ++
+ .../tests/exception-handler-fail.phpt         |   41 +
+ .../tests/exception-handler-options.phpt      |   54 +
+ ext/pthreads/tests/exception-handler.phpt     |   79 ++
+ ext/pthreads/tests/explicit-cast-arrays.phpt  |   47 +
+ .../tests/foreign-private-members.phpt        |   31 +
+ ext/pthreads/tests/functions.phpt             |   21 +
+ ext/pthreads/tests/global-constants.phpt      |   36 +
+ ext/pthreads/tests/gone.phpt                  |   34 +
+ ext/pthreads/tests/graceful-fatalities.phpt   |   24 +
+ ext/pthreads/tests/hacking-serials.phpt       |   51 +
+ .../tests/immutable-threaded-members.phpt     |   86 ++
+ .../tests/include_interface_constant.phpt     |   26 +
+ ext/pthreads/tests/included-files.phpt        |   22 +
+ ext/pthreads/tests/includeme.inc              |    2 +
+ ext/pthreads/tests/inherit-non-closures.phpt  |   19 +
+ .../inherited-anon-class-outside-context.phpt |   73 ++
+ ext/pthreads/tests/inherited-anon-class.phpt  |   17 +
+ ext/pthreads/tests/ini.phpt                   |   17 +
+ ext/pthreads/tests/interface.phpt             |   28 +
+ ext/pthreads/tests/interfaces.phpt            |   36 +
+ ext/pthreads/tests/ipv6_skipif.inc            |    6 +
+ ext/pthreads/tests/isset-empty-compat.phpt    |   93 ++
+ ext/pthreads/tests/isset-unset.phpt           |   86 ++
+ ext/pthreads/tests/iterator-basic.phpt        |   34 +
+ ext/pthreads/tests/iterator-funcs.phpt        |   54 +
+ ext/pthreads/tests/iterator-no-refs.phpt      |   15 +
+ ext/pthreads/tests/legacy-ctor.phpt           |   24 +
+ ext/pthreads/tests/lexical-vars.phpt          |   69 ++
+ ext/pthreads/tests/magic-methods-get-set.phpt |   30 +
+ .../tests/magic-methods-isset-unset.phpt      |   32 +
+ ext/pthreads/tests/merge-non-threaded.phpt    |   53 +
+ ext/pthreads/tests/merging-members.phpt       |   48 +
+ ext/pthreads/tests/merging-range.phpt         |   16 +
+ ...nested-non-threaded-object-references.phpt |   28 +
+ .../new-class-after-thread-creation.phpt      |   18 +
+ ext/pthreads/tests/no-cloning.phpt            |   16 +
+ .../tests/no-inheritance-anon-class.phpt      |   16 +
+ ext/pthreads/tests/norefs.phpt                |   57 +
+ ext/pthreads/tests/normal-reads.phpt          |   20 +
+ ext/pthreads/tests/normalize-members.phpt     |   22 +
+ ext/pthreads/tests/null-member-crash.phpt     |   18 +
+ ext/pthreads/tests/object-cast.phpt           |   50 +
+ ext/pthreads/tests/object-comparison.phpt     |   41 +
+ ext/pthreads/tests/oomethods.phpt             |   23 +
+ ext/pthreads/tests/pools-default.phpt         |   44 +
+ ext/pthreads/tests/pools.phpt                 |   72 ++
+ .../private-class-constant-fetching.phpt      |   50 +
+ ...inherit-none-class-constant-inherited.phpt |   29 +
+ .../pthreads-inherit-none-class-constant.phpt |   20 +
+ ext/pthreads/tests/recv-overload.phpt         |   46 +
+ ext/pthreads/tests/resource-keeping.phpt      |  149 +++
+ ext/pthreads/tests/return-types.phpt          |   20 +
+ .../tests/run-time-cache-corruption.phpt      |   58 +
+ .../tests/runtime-inheritance-volatile.phpt   |   23 +
+ ext/pthreads/tests/runtime-inheritance.phpt   |   23 +
+ ext/pthreads/tests/selective-inheritance.phpt |   49 +
+ ext/pthreads/tests/shift-pop.phpt             |  342 ++++++
+ ext/pthreads/tests/shutdown-handler.phpt      |   21 +
+ ext/pthreads/tests/socket-bind.phpt           |   38 +
+ .../tests/socket-connect-error-unix.phpt      |   43 +
+ .../tests/socket-connect-error-win32.phpt     |   44 +
+ .../tests/socket-connect-params-af-inet.phpt  |   21 +
+ .../tests/socket-connect-params-af-unix.phpt  |   32 +
+ ext/pthreads/tests/socket-construct.phpt      |   14 +
+ ext/pthreads/tests/socket-getoption.phpt      |   18 +
+ .../tests/socket-getpeername-af-inet.phpt     |   32 +
+ .../tests/socket-getpeername-af-unix.phpt     |   30 +
+ .../tests/socket-getsockname-af-inet.phpt     |   27 +
+ .../tests/socket-getsockname-af-unix.phpt     |   28 +
+ ext/pthreads/tests/socket-ipv4loop.phpt       |   55 +
+ ext/pthreads/tests/socket-ipv6loop.phpt       |   59 +
+ ext/pthreads/tests/socket-read-type.phpt      |   67 +
+ ext/pthreads/tests/socket-select-unix.phpt    |   45 +
+ ext/pthreads/tests/socket-select-win32.phpt   |   45 +
+ .../socket-sentto-recvfrom-ipv4-udp.phpt      |   54 +
+ .../socket-sentto-recvfrom-ipv6-udp.phpt      |   56 +
+ .../tests/socket-sentto-recvfrom-unix.phpt    |   65 +
+ ext/pthreads/tests/socket-setoption.phpt      |   20 +
+ ext/pthreads/tests/socket-unixloop.phpt       |   56 +
+ ext/pthreads/tests/stateful-fatalities.phpt   |   24 +
+ .../static-inheritance-unexpected-null.phpt   |   28 +
+ ext/pthreads/tests/static-resolution.phpt     |   31 +
+ ext/pthreads/tests/static-scope-vars.phpt     |   53 +
+ ext/pthreads/tests/staticmethods.phpt         |   22 +
+ .../tests/statics-thorn-in-other-side.phpt    |   58 +
+ ext/pthreads/tests/statics.phpt               |   17 +
+ ext/pthreads/tests/synchronization.phpt       |   25 +
+ ext/pthreads/tests/thorn-in-side.phpt         |   31 +
+ ext/pthreads/tests/thread.phpt                |   17 +
+ .../tests/threaded-extend-abstract.phpt       |   10 +
+ ext/pthreads/tests/threaded_valid_tsrmls.phpt |   36 +
+ ext/pthreads/tests/trait-alias-bug.phpt       |   31 +
+ .../tests/trait-aliases-reflection.phpt       |   28 +
+ ext/pthreads/tests/trait-aliases.phpt         |   26 +
+ ext/pthreads/tests/traits-scope.inc           |   10 +
+ ext/pthreads/tests/traits-scope.phpt          |   27 +
+ .../unprepared-entry-static-segfault.phpt     |   31 +
+ ext/pthreads/tests/unset-defaults.phpt        |   19 +
+ ext/pthreads/tests/unstack-running.phpt       |   27 +
+ ext/pthreads/tests/verify-overload.phpt       |   53 +
+ ext/pthreads/tests/volatile-arrays.phpt       |   99 ++
+ ext/pthreads/tests/volatile-nested.phpt       |   90 ++
+ ext/pthreads/tests/volatile-objects.phpt      |   70 ++
+ ext/pthreads/tests/wait.phpt                  |   32 +
+ ext/pthreads/tests/waiting-timeouts.phpt      |   35 +
+ ext/pthreads/tests/workers-constant-copy.phpt |   24 +
+ .../tests/workers-default-collector.phpt      |   33 +
+ ext/pthreads/tests/workers-no-collect.phpt    |   31 +
+ ext/pthreads/tests/workers-no-join.phpt       |   31 +
+ .../workers-no-shutdown-before-start.phpt     |   22 +
+ ext/pthreads/tests/workers-no-stack.phpt      |   36 +
+ ext/pthreads/tests/workers-no-start.phpt      |   30 +
+ ext/pthreads/tests/workers-no-unstack.phpt    |   32 +
+ ext/pthreads/tests/workers-ok-unstack.phpt    |   19 +
+ 203 files changed, 17294 insertions(+)
+ create mode 100644 ext/pthreads/.github/ISSUE_TEMPLATE.md
+ create mode 100644 ext/pthreads/.gitignore
+ create mode 100644 ext/pthreads/LICENSE
+ create mode 100644 ext/pthreads/README.md
+ create mode 100644 ext/pthreads/appveyor.yml
+ create mode 100644 ext/pthreads/classes/collectable.h
+ create mode 100644 ext/pthreads/classes/pool.h
+ create mode 100644 ext/pthreads/classes/socket.h
+ create mode 100644 ext/pthreads/classes/thread.h
+ create mode 100644 ext/pthreads/classes/threaded.h
+ create mode 100644 ext/pthreads/classes/worker.h
+ create mode 100644 ext/pthreads/config.m4
+ create mode 100644 ext/pthreads/config.w32
+ create mode 100644 ext/pthreads/examples/Benchmark.php
+ create mode 100644 ext/pthreads/examples/CallAnyFunction.php
+ create mode 100644 ext/pthreads/examples/ChannelsAndMagic.php
+ create mode 100644 ext/pthreads/examples/ClosureFuture.php
+ create mode 100644 ext/pthreads/examples/KeepAliveSession.php
+ create mode 100644 ext/pthreads/examples/MySQLi.php
+ create mode 100644 ext/pthreads/examples/NonBlockingSocketServer.php
+ create mode 100644 ext/pthreads/examples/ObjectsAsParameters.php
+ create mode 100644 ext/pthreads/examples/Pooling.php
+ create mode 100644 ext/pthreads/examples/SelectiveInheritance.php
+ create mode 100644 ext/pthreads/examples/SharingResources.php
+ create mode 100644 ext/pthreads/examples/SimpleWebRequest.php
+ create mode 100644 ext/pthreads/examples/Singleton.php
+ create mode 100644 ext/pthreads/examples/SocketServer.php
+ create mode 100644 ext/pthreads/examples/Sockets.php
+ create mode 100644 ext/pthreads/examples/StackableArray.php
+ create mode 100644 ext/pthreads/examples/Synchronization.php
+ create mode 100644 ext/pthreads/examples/fetching-data-from-a-thread.php
+ create mode 100644 ext/pthreads/examples/stub.php
+ create mode 100644 ext/pthreads/package.xml
+ create mode 100644 ext/pthreads/php_pthreads.c
+ create mode 100644 ext/pthreads/php_pthreads.h
+ create mode 100644 ext/pthreads/src/copy.h
+ create mode 100644 ext/pthreads/src/globals.c
+ create mode 100644 ext/pthreads/src/globals.h
+ create mode 100644 ext/pthreads/src/handlers.c
+ create mode 100644 ext/pthreads/src/handlers.h
+ create mode 100644 ext/pthreads/src/monitor.c
+ create mode 100644 ext/pthreads/src/monitor.h
+ create mode 100644 ext/pthreads/src/object.c
+ create mode 100644 ext/pthreads/src/object.h
+ create mode 100644 ext/pthreads/src/prepare.c
+ create mode 100644 ext/pthreads/src/prepare.h
+ create mode 100644 ext/pthreads/src/pthreads.h
+ create mode 100644 ext/pthreads/src/resources.c
+ create mode 100644 ext/pthreads/src/resources.h
+ create mode 100644 ext/pthreads/src/socket.c
+ create mode 100644 ext/pthreads/src/socket.h
+ create mode 100644 ext/pthreads/src/sockets/windows_common.h
+ create mode 100644 ext/pthreads/src/stack.c
+ create mode 100644 ext/pthreads/src/stack.h
+ create mode 100644 ext/pthreads/src/store.c
+ create mode 100644 ext/pthreads/src/store.h
+ create mode 100644 ext/pthreads/src/thread.h
+ create mode 100644 ext/pthreads/tests/anon-bound-inherited.phpt
+ create mode 100644 ext/pthreads/tests/anon-interceptors.phpt
+ create mode 100644 ext/pthreads/tests/anon-unbound-double-inherited.phpt
+ create mode 100644 ext/pthreads/tests/anon-unbound-inherited.phpt
+ create mode 100644 ext/pthreads/tests/anon-unbound-use-guards.phpt
+ create mode 100644 ext/pthreads/tests/binding.phpt
+ create mode 100644 ext/pthreads/tests/callstatic-etc.phpt
+ create mode 100644 ext/pthreads/tests/child-to-parent-class-copying.phpt
+ create mode 100644 ext/pthreads/tests/chunk.phpt
+ create mode 100644 ext/pthreads/tests/class-defaults.phpt
+ create mode 100644 ext/pthreads/tests/class-static-properties-2.phpt
+ create mode 100644 ext/pthreads/tests/class-static-properties-double-inheritance.phpt
+ create mode 100644 ext/pthreads/tests/class-static-properties-order-dependencies-inheritance.phpt
+ create mode 100644 ext/pthreads/tests/class-static-properties-order-dependencies.phpt
+ create mode 100644 ext/pthreads/tests/class-static-properties.phpt
+ create mode 100644 ext/pthreads/tests/closure-delayed-include.phpt
+ create mode 100644 ext/pthreads/tests/closure-secondary-thread-copy.phpt
+ create mode 100644 ext/pthreads/tests/closures-as-members-repeated-reallocation.phpt
+ create mode 100644 ext/pthreads/tests/closures-as-members.phpt
+ create mode 100644 ext/pthreads/tests/complex-statics-set-null.phpt
+ create mode 100644 ext/pthreads/tests/complex-statics.phpt
+ create mode 100644 ext/pthreads/tests/constant-array.phpt
+ create mode 100644 ext/pthreads/tests/constant-ast-copy-missing-type.phpt
+ create mode 100644 ext/pthreads/tests/constant-ast-copy.phpt
+ create mode 100644 ext/pthreads/tests/constants.phpt
+ create mode 100644 ext/pthreads/tests/corrupted-resources.phpt
+ create mode 100644 ext/pthreads/tests/counting-members.phpt
+ create mode 100644 ext/pthreads/tests/crazy-refcounting-stuff.phpt
+ create mode 100644 ext/pthreads/tests/display-startup-errors-output.phpt
+ create mode 100644 ext/pthreads/tests/doc-comments.phpt
+ create mode 100644 ext/pthreads/tests/exception-handler-caught-nested.phpt
+ create mode 100644 ext/pthreads/tests/exception-handler-caught-uncaught.phpt
+ create mode 100644 ext/pthreads/tests/exception-handler-fail.phpt
+ create mode 100644 ext/pthreads/tests/exception-handler-options.phpt
+ create mode 100644 ext/pthreads/tests/exception-handler.phpt
+ create mode 100644 ext/pthreads/tests/explicit-cast-arrays.phpt
+ create mode 100644 ext/pthreads/tests/foreign-private-members.phpt
+ create mode 100644 ext/pthreads/tests/functions.phpt
+ create mode 100644 ext/pthreads/tests/global-constants.phpt
+ create mode 100644 ext/pthreads/tests/gone.phpt
+ create mode 100644 ext/pthreads/tests/graceful-fatalities.phpt
+ create mode 100644 ext/pthreads/tests/hacking-serials.phpt
+ create mode 100644 ext/pthreads/tests/immutable-threaded-members.phpt
+ create mode 100644 ext/pthreads/tests/include_interface_constant.phpt
+ create mode 100644 ext/pthreads/tests/included-files.phpt
+ create mode 100644 ext/pthreads/tests/includeme.inc
+ create mode 100644 ext/pthreads/tests/inherit-non-closures.phpt
+ create mode 100644 ext/pthreads/tests/inherited-anon-class-outside-context.phpt
+ create mode 100644 ext/pthreads/tests/inherited-anon-class.phpt
+ create mode 100644 ext/pthreads/tests/ini.phpt
+ create mode 100644 ext/pthreads/tests/interface.phpt
+ create mode 100644 ext/pthreads/tests/interfaces.phpt
+ create mode 100644 ext/pthreads/tests/ipv6_skipif.inc
+ create mode 100644 ext/pthreads/tests/isset-empty-compat.phpt
+ create mode 100644 ext/pthreads/tests/isset-unset.phpt
+ create mode 100644 ext/pthreads/tests/iterator-basic.phpt
+ create mode 100644 ext/pthreads/tests/iterator-funcs.phpt
+ create mode 100644 ext/pthreads/tests/iterator-no-refs.phpt
+ create mode 100644 ext/pthreads/tests/legacy-ctor.phpt
+ create mode 100644 ext/pthreads/tests/lexical-vars.phpt
+ create mode 100644 ext/pthreads/tests/magic-methods-get-set.phpt
+ create mode 100644 ext/pthreads/tests/magic-methods-isset-unset.phpt
+ create mode 100644 ext/pthreads/tests/merge-non-threaded.phpt
+ create mode 100644 ext/pthreads/tests/merging-members.phpt
+ create mode 100644 ext/pthreads/tests/merging-range.phpt
+ create mode 100644 ext/pthreads/tests/nested-non-threaded-object-references.phpt
+ create mode 100644 ext/pthreads/tests/new-class-after-thread-creation.phpt
+ create mode 100644 ext/pthreads/tests/no-cloning.phpt
+ create mode 100644 ext/pthreads/tests/no-inheritance-anon-class.phpt
+ create mode 100644 ext/pthreads/tests/norefs.phpt
+ create mode 100644 ext/pthreads/tests/normal-reads.phpt
+ create mode 100644 ext/pthreads/tests/normalize-members.phpt
+ create mode 100644 ext/pthreads/tests/null-member-crash.phpt
+ create mode 100644 ext/pthreads/tests/object-cast.phpt
+ create mode 100644 ext/pthreads/tests/object-comparison.phpt
+ create mode 100644 ext/pthreads/tests/oomethods.phpt
+ create mode 100644 ext/pthreads/tests/pools-default.phpt
+ create mode 100644 ext/pthreads/tests/pools.phpt
+ create mode 100644 ext/pthreads/tests/private-class-constant-fetching.phpt
+ create mode 100644 ext/pthreads/tests/pthreads-inherit-none-class-constant-inherited.phpt
+ create mode 100644 ext/pthreads/tests/pthreads-inherit-none-class-constant.phpt
+ create mode 100644 ext/pthreads/tests/recv-overload.phpt
+ create mode 100644 ext/pthreads/tests/resource-keeping.phpt
+ create mode 100644 ext/pthreads/tests/return-types.phpt
+ create mode 100644 ext/pthreads/tests/run-time-cache-corruption.phpt
+ create mode 100644 ext/pthreads/tests/runtime-inheritance-volatile.phpt
+ create mode 100644 ext/pthreads/tests/runtime-inheritance.phpt
+ create mode 100644 ext/pthreads/tests/selective-inheritance.phpt
+ create mode 100644 ext/pthreads/tests/shift-pop.phpt
+ create mode 100644 ext/pthreads/tests/shutdown-handler.phpt
+ create mode 100644 ext/pthreads/tests/socket-bind.phpt
+ create mode 100644 ext/pthreads/tests/socket-connect-error-unix.phpt
+ create mode 100644 ext/pthreads/tests/socket-connect-error-win32.phpt
+ create mode 100644 ext/pthreads/tests/socket-connect-params-af-inet.phpt
+ create mode 100644 ext/pthreads/tests/socket-connect-params-af-unix.phpt
+ create mode 100644 ext/pthreads/tests/socket-construct.phpt
+ create mode 100644 ext/pthreads/tests/socket-getoption.phpt
+ create mode 100644 ext/pthreads/tests/socket-getpeername-af-inet.phpt
+ create mode 100644 ext/pthreads/tests/socket-getpeername-af-unix.phpt
+ create mode 100644 ext/pthreads/tests/socket-getsockname-af-inet.phpt
+ create mode 100644 ext/pthreads/tests/socket-getsockname-af-unix.phpt
+ create mode 100644 ext/pthreads/tests/socket-ipv4loop.phpt
+ create mode 100644 ext/pthreads/tests/socket-ipv6loop.phpt
+ create mode 100644 ext/pthreads/tests/socket-read-type.phpt
+ create mode 100644 ext/pthreads/tests/socket-select-unix.phpt
+ create mode 100644 ext/pthreads/tests/socket-select-win32.phpt
+ create mode 100644 ext/pthreads/tests/socket-sentto-recvfrom-ipv4-udp.phpt
+ create mode 100644 ext/pthreads/tests/socket-sentto-recvfrom-ipv6-udp.phpt
+ create mode 100644 ext/pthreads/tests/socket-sentto-recvfrom-unix.phpt
+ create mode 100644 ext/pthreads/tests/socket-setoption.phpt
+ create mode 100644 ext/pthreads/tests/socket-unixloop.phpt
+ create mode 100644 ext/pthreads/tests/stateful-fatalities.phpt
+ create mode 100644 ext/pthreads/tests/static-inheritance-unexpected-null.phpt
+ create mode 100644 ext/pthreads/tests/static-resolution.phpt
+ create mode 100644 ext/pthreads/tests/static-scope-vars.phpt
+ create mode 100644 ext/pthreads/tests/staticmethods.phpt
+ create mode 100644 ext/pthreads/tests/statics-thorn-in-other-side.phpt
+ create mode 100644 ext/pthreads/tests/statics.phpt
+ create mode 100644 ext/pthreads/tests/synchronization.phpt
+ create mode 100644 ext/pthreads/tests/thorn-in-side.phpt
+ create mode 100644 ext/pthreads/tests/thread.phpt
+ create mode 100644 ext/pthreads/tests/threaded-extend-abstract.phpt
+ create mode 100644 ext/pthreads/tests/threaded_valid_tsrmls.phpt
+ create mode 100644 ext/pthreads/tests/trait-alias-bug.phpt
+ create mode 100644 ext/pthreads/tests/trait-aliases-reflection.phpt
+ create mode 100644 ext/pthreads/tests/trait-aliases.phpt
+ create mode 100644 ext/pthreads/tests/traits-scope.inc
+ create mode 100644 ext/pthreads/tests/traits-scope.phpt
+ create mode 100644 ext/pthreads/tests/unprepared-entry-static-segfault.phpt
+ create mode 100644 ext/pthreads/tests/unset-defaults.phpt
+ create mode 100644 ext/pthreads/tests/unstack-running.phpt
+ create mode 100644 ext/pthreads/tests/verify-overload.phpt
+ create mode 100644 ext/pthreads/tests/volatile-arrays.phpt
+ create mode 100644 ext/pthreads/tests/volatile-nested.phpt
+ create mode 100644 ext/pthreads/tests/volatile-objects.phpt
+ create mode 100644 ext/pthreads/tests/wait.phpt
+ create mode 100644 ext/pthreads/tests/waiting-timeouts.phpt
+ create mode 100644 ext/pthreads/tests/workers-constant-copy.phpt
+ create mode 100644 ext/pthreads/tests/workers-default-collector.phpt
+ create mode 100644 ext/pthreads/tests/workers-no-collect.phpt
+ create mode 100644 ext/pthreads/tests/workers-no-join.phpt
+ create mode 100644 ext/pthreads/tests/workers-no-shutdown-before-start.phpt
+ create mode 100644 ext/pthreads/tests/workers-no-stack.phpt
+ create mode 100644 ext/pthreads/tests/workers-no-start.phpt
+ create mode 100644 ext/pthreads/tests/workers-no-unstack.phpt
+ create mode 100644 ext/pthreads/tests/workers-ok-unstack.phpt
+
+diff --git a/ext/pthreads/.github/ISSUE_TEMPLATE.md b/ext/pthreads/.github/ISSUE_TEMPLATE.md
+new file mode 100644
+index 00000000..6efea898
+--- /dev/null
++++ b/ext/pthreads/.github/ISSUE_TEMPLATE.md
+@@ -0,0 +1,34 @@
++*If you know how to write phpt tests, please open a PR with environment information, and your test, this will cause CI to test your issue before a human is able to look*
++
++Environment
++==========
++
++  * PHP: *can be obtained with ```php -v```*
++  * pthreads: *can be obtained with ```php -dextension=pthreads.so --ri pthreads | grep Version```*
++  * OS: *if Windows, include arch*
++
++Summary
++======
++
++A summary isn't always necessary, code > words ... delete this section, as applicable.
++
++Reproducing Code
++==============
++
++Bug reports *must* come with reproducing code, please ensure code is formatted correctly.
++
++If the code cannot be a single script, please link to a gist, or a github repository containing the reproducing code. 
++
++Please, do not link to application repositories.
++
++*Please ensure code is self contained, and executable: if the code cannot be executed, the bug cannot be reproduced.*
++
++Expected Output
++=============
++
++Please include the expected output, properly formatted.
++
++Actual Output
++============
++
++Please include the actual output, properly formatted.
+diff --git a/ext/pthreads/.gitignore b/ext/pthreads/.gitignore
+new file mode 100644
+index 00000000..3a4edf69
+--- /dev/null
++++ b/ext/pthreads/.gitignore
+@@ -0,0 +1 @@
++.project
+diff --git a/ext/pthreads/LICENSE b/ext/pthreads/LICENSE
+new file mode 100644
+index 00000000..a240ec6b
+--- /dev/null
++++ b/ext/pthreads/LICENSE
+@@ -0,0 +1,65 @@
++-------------------------------------------------------------------- 
++                  The PHP License, version 3.01
++Copyright (c) 2012-2014 Joe Watkins. All rights reserved.
++-------------------------------------------------------------------- 
++
++Redistribution and use in source and binary forms, with or without
++modification, is permitted provided that the following conditions
++are met:
++
++  1. Redistributions of source code must retain the above copyright
++     notice, this list of conditions and the following disclaimer.
++ 
++  2. Redistributions in binary form must reproduce the above copyright
++     notice, this list of conditions and the following disclaimer in
++     the documentation and/or other materials provided with the
++     distribution.
++ 
++  3. The name "PHP" must not be used to endorse or promote products
++     derived from this software without prior written permission. For
++     written permission, please contact group@php.net.
++  
++  4. Products derived from this software may not be called "PHP", nor
++     may "PHP" appear in their name, without prior written permission
++     from group@php.net.  You may indicate that your software works in
++     conjunction with PHP by saying "Foo for PHP" instead of calling
++     it "PHP Foo" or "phpfoo"
++ 
++  5. The PHP Group may publish revised and/or new versions of the
++     license from time to time. Each version will be given a
++     distinguishing version number.
++     Once covered code has been published under a particular version
++     of the license, you may always continue to use it under the terms
++     of that version. You may also choose to use such covered code
++     under the terms of any subsequent version of the license
++     published by the PHP Group. No one other than the PHP Group has
++     the right to modify the terms applicable to covered code created
++     under this License.
++
++  6. Redistributions of any form whatsoever must retain the following
++     acknowledgment:
++     "This product includes PHP software, freely available from
++     <http://www.php.net/software/>".
++
++THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 
++ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
++THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
++PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PHP
++DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
++INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
++(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
++SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++OF THE POSSIBILITY OF SUCH DAMAGE.
++
++-------------------------------------------------------------------- 
++
++The PHP Group can be contacted via Email at group@php.net.
++
++For more information on the PHP Group and the PHP project, 
++please see <http://www.php.net>.
++
++PHP includes the Zend Engine, freely available at
++<http://www.zend.com>.
+diff --git a/ext/pthreads/README.md b/ext/pthreads/README.md
+new file mode 100644
+index 00000000..d6c97038
+--- /dev/null
++++ b/ext/pthreads/README.md
+@@ -0,0 +1,199 @@
++# Threading for PHP - Share Nothing, Do Everything :)
++
++[![Build Status](https://travis-ci.org/krakjoe/pthreads.svg?branch=master)](https://travis-ci.org/krakjoe/pthreads)
++[![Build status](https://ci.appveyor.com/api/projects/status/5pyrhbrxcgenkpbk/branch/master?svg=true)](https://ci.appveyor.com/project/krakjoe/pthreads/branch/master)
++[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/krakjoe/pthreads.svg)](http://isitmaintained.com/project/krakjoe/pthreads "Average time to resolve an issue")
++[![Percentage of issues still open](http://isitmaintained.com/badge/open/krakjoe/pthreads.svg)](http://isitmaintained.com/project/krakjoe/pthreads "Percentage of issues still open")
++[![Join the chat at https://gitter.im/krakjoe/pthreads](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/krakjoe/pthreads?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
++
++This project provides multi-threading that is compatible with PHP based on Posix Threads.
++
++## Highlights
++
++* An easy to use, quick to learn OO Threading API for PHP 7.2+
++* Execute any and all predefined and user declared methods and functions, including closures.
++* Ready made synchronization included
++* A world of possibilities ...
++
++## Technical Features
++
++* High Level Threading
++* Synchronization
++* Worker Threads
++* Thread Pools
++* Complete Support for OO - ie. traits, interfaces, inheritance etc
++* Full read/write/execute support for Threaded objects
++
++## Requirements
++
++* PHP 7.2+
++* ZTS Enabled ( Thread Safety )
++* Posix Threads Implementation
++
++Testing has been carried out on x86, x64 and ARM, in general you just need a compiler and pthread.h
++
++## PHP7
++
++For PHP7, pthreads has been almost completely rewritten to be more efficient, easier to use and more robust. I will give a brief changelog here:
++
++The API for v3 has changed, the following things have been removed:
++
++ * ```Mutex```, ```Cond```, and ```Stackable```
++ * ```Threaded::lock``` and ```Threaded::unlock```
++ * ```Threaded::isWaiting```
++ * ```Threaded::from```
++ * ```Thread::kill``` (there be dragons)
++ * ```Thread::detach```
++ * ```Worker::isWorking```
++ * ```Threaded::getTerminationInfo``` (this was unsafe, a better, safe impl can be done in userland)
++ * Special behaviour of ```protected``` and ```private``` methods on ```Threaded``` objects
++
++The following things have significant changes:
++ 
++ * The method by which ```Threaded``` objects are stored as member properties of other ```Threaded``` objects.
++ * The structure used by a ```Worker``` for stack (```Collectable``` objects to execute inserted by ```Worker::stack```).
++ * The ```Pool::collect``` mechanism was moved from ```Pool``` to ```Worker``` for a more robust ```Worker``` and simpler ```Pool``` inheritance.
++ * The method by which iteration occurs on ```Threaded``` objects, such that it uses memory more efficiently.
++ * ```Threaded::synchronized``` provides true synchronization (state and properties lock).
++ * ```Worker``` objects no longer require that you retain a reference to ```Collectable``` objects on the stack.
++ * Unified monitor (cond/mutex/state) for ```Threaded``` objects
++ * ```Threaded``` members of ```Threaded``` objects are immutable
++ * ```Volatile``` objects, exempt from immutability
++ * ```array``` coerced to ```Volatile``` when set as member of ```Threaded```
++ * ```Collectable``` converted to interface, to make ```extends Volatile implements Collectable``` possible.
++
++Some blog posts explaining these changes:
++
++ * [A Letter from the Future](http://blog.krakjoe.ninja/2015/08/a-letter-from-future.html)
++ * [Addendum to A Letter from the Future](http://blog.krakjoe.ninja/2015/09/addendum-letter-from-future.html)
++
++### Supported PHP Versions
++
++pthreads v3 requires PHP7 or above. PHP5 needs to use pthreads v2 which can be found in the PHP5 branch.
++
++Note that only PHP 7.2+ is now supported (requiring the current master branch of pthreads). This is due to safety issues with ZTS mode on PHP 7.0 and 7.1.
++
++##### Unix-based Building from Source
++
++Building pthreads from source is quite simple on Unix-based OSs. The instructions are as follows:
++ * Clone this repository and checkout the release to use (or master for the latest updates)
++ * `cd pthreads`
++ * `phpize`
++ * `./configure`
++ * `make`
++ * `make install` (may need sudo)
++ * Update your php.ini file to load the `pthreads.so` file using the `extension` directive
++
++### Windows Support
++
++Yes !! Windows support is offered thanks to the pthread-w32 library.
++
++Releases for Windows can be found: http://windows.php.net/downloads/pecl/releases/pthreads/
++
++##### Simple Windows Installation
++
++* Add `pthreadVC2.dll` (included with the Windows releases) to the same directory as `php.exe` eg. `C:\xampp\php`
++* Add `php_pthreads.dll` to PHP extension folder eg. `C:\xampp\php\ext`
++
++### Mac OSX Support
++
++Yes !! Users of Mac will be glad to hear that pthreads is now tested on OSX as part of the development process.
++
++### Hello World
++
++As is customary in our line of work:
++
++```php
++<?php
++$thread = new class extends Thread {
++	public function run() {
++		echo "Hello World\n";
++	}
++};
++
++$thread->start() && $thread->join();
++?>
++```
++
++### Are you serious ?
++
++Absolutely, this is not a hack, we _don't_ use forking or any other such nonsense, what you create are honest to goodness posix threads that are completely compatible with PHP and safe ... this is true multi-threading :)
++
++### SAPI Support
++
++pthreads v3 is restricted to operating in CLI only: I have spent many years trying to explain that threads in a web server just don't make sense, after 1,111 commits to pthreads I have realised that, my advice is going unheeded.
++
++So I'm promoting the advice to hard and fast fact: *you can't use pthreads safely and sensibly anywhere but CLI.*
++
++Thanks for listening ;)
++
++### Documentation 
++
++Documentation can be found in the PHP manual: http://docs.php.net/manual/en/book.pthreads.php, and some examples can be found in the "examples" folder in the master repository.
++
++Further insights and occasional announcements can be read at the http://pthreads.org site where pthreads is developed and tested in the real world.
++
++Here are some links to articles I have prepared for users: everybody should read them before they do anything else:
++
++ - https://gist.github.com/krakjoe/6437782
++ - https://gist.github.com/krakjoe/9384409
++
++If you have had the time to put any cool demo's together and would like them showcased on pthreads.org please get in touch.
++
++### Polyfill
++
++It's possible to write code that optionally takes advantage of parallelism where the environment has ```pthreads``` loaded.
++
++This is made possible by [pthreads-polyfill](http://github.com/krakjoe/pthreads-polyfill) which can be found on [packagist](https://packagist.org/packages/krakjoe/pthreads-polyfill).
++
++Having required the appropriate package in your composer.json, the following code is executable everywhere:
++
++```php
++<?php
++require_once("vendor/autoload.php");
++
++if (extension_loaded("pthreads")) {
++	    echo "Using pthreads\n";
++} else  echo "Using polyfill\n";
++
++$pool = new Pool(4);
++
++$pool->submit(new class extends Threaded {
++        public function run() {
++                echo "Hello World\n";
++        }
++});
++
++while ($pool->collect()) continue;
++
++$pool->shutdown();
++?>
++```
++
++Some guidance on getting started, and detail regarding how the polyfill came to exist can be found [here](http://blog.krakjoe.ninja/2015/09/what-polly-really-wants.html).
++
++### Feedback
++
++Please submit issues, and send your feedback and suggestions as often as you have them.
++
++### Reporting Bugs
++
++If you believe you have found a bug in pthreads, please open an issue: Include in your report *minimal, executable, reproducing code*.
++
++Minimal:     reduce your problem to the smallest amount of code possible; This helps with hunting the bug, but also it helps with integration and regression testing once the bug is fixed.
++
++Executable:  include all the information required to execute the example code, code snippets are not helpful.
++
++Reproducing: some bugs don't show themselves on every execution, that's fine, mention that in the report and give an idea of how often you encounter the bug.
++
++__It is impossible to help without reproducing code, bugs that are opened without reproducing code will be closed.__
++
++Please include version and operating system information in your report.
++
++*Please do not post requests to help with code on github; I spend a lot of time on Stackoverflow, a much better place for asking questions.*
++
++Have patience; I am one human being.
++
++### Developers
++
++There is no defined API for you to create your own threads in your extensions, this project aims to provide Userland threading, it does not aim to provide a threading API for extension developers. I suggest you allow users to decide what they thread and keep your own extension focused on your functionality.
+diff --git a/ext/pthreads/appveyor.yml b/ext/pthreads/appveyor.yml
+new file mode 100644
+index 00000000..c5a53b4c
+--- /dev/null
++++ b/ext/pthreads/appveyor.yml
+@@ -0,0 +1,91 @@
++version: '{branch}.{build}'
++branches:
++  only:
++  - master
++
++image: Visual Studio 2017
++
++environment:
++  PHP_SDK_BINARY_TOOLS_VER: php-sdk-2.1.1
++
++  matrix:
++    - ARCH: x86
++      PHP_VER: 7.2
++      VC_VER: vc15
++    - ARCH: x64
++      PHP_VER: 7.2
++      VC_VER: vc15
++    - ARCH: x86
++      PHP_VER: 7.3
++      VC_VER: vc15
++    - ARCH: x64
++      PHP_VER: 7.3
++      VC_VER: vc15
++install:
++- cmd: cinst wget
++build_script:
++- cmd: >-
++    "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" %ARCH%
++
++    wget https://github.com/OSTC/php-sdk-binary-tools/archive/%PHP_SDK_BINARY_TOOLS_VER%.zip --no-check-certificate -q -O php-sdk-binary-tools-%PHP_SDK_BINARY_TOOLS_VER%.zip
++
++    7z x -y php-sdk-binary-tools-%PHP_SDK_BINARY_TOOLS_VER%.zip -oC:\projects
++
++    move C:\projects\php-sdk-binary-tools-%PHP_SDK_BINARY_TOOLS_VER% C:\projects\php-sdk
++    
++    C:\projects\php-sdk\bin\phpsdk_setvars.bat
++
++    git clone https://github.com/php/php-src C:\projects\php-src -b PHP-%PHP_VER% --depth=1
++
++    mkdir C:\projects\php-src\ext\pthreads
++
++    xcopy C:\projects\pthreads C:\projects\php-src\ext\pthreads /s /e /y /q
++
++    phpsdk_deps -u -t %VC_VER% -b %PHP_VER% -a %ARCH% -f -d C:\projects\php-src\deps
++
++    wget http://www.mirrorservice.org/sites/sources.redhat.com/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip -q
++
++    7z x -y pthreads-w32-2-9-1-release.zip -oC:\projects\pthreads-w32
++
++    copy C:\projects\pthreads-w32\Pre-built.2\include\pthread.h C:\projects\php-src\deps\include\pthread.h
++
++    copy C:\projects\pthreads-w32\Pre-built.2\include\sched.h C:\projects\php-src\deps\include\sched.h
++
++    copy C:\projects\pthreads-w32\Pre-built.2\include\semaphore.h C:\projects\php-src\deps\include\semaphore.h
++
++    copy C:\projects\pthreads-w32\Pre-built.2\lib\%ARCH%\pthreadVC2.lib C:\projects\php-src\deps\lib\pthreadVC2.lib
++
++    cd C:\projects\php-src
++
++    buildconf.bat
++
++    configure.bat --disable-all --enable-cli --enable-zts --with-pthreads=shared --with-prefix=C:\projects\pthreads\bin --with-php-build=deps --enable-ipv6
++
++    nmake
++
++    nmake install
++
++    cd C:\projects\pthreads\bin
++
++    copy C:\projects\pthreads-w32\Pre-built.2\dll\%ARCH%\pthreadVC2.dll C:\projects\pthreads\bin\pthreadVC2.dll
++
++    echo [PHP] > php.ini
++
++    echo extension_dir = "ext" >> php.ini
++
++    echo extension=php_pthreads.dll >> php.ini
++
++    set TEST_PHP_EXECUTABLE=%cd%\php.exe
++
++    php -v
++
++    php -m
++test_script:
++- cmd: >-
++    set REPORT_EXIT_STATUS=1
++
++    php.exe /projects/php-src/run-tests.php /projects/php-src/ext/pthreads -q --show-diff --offline
++artifacts:
++  - path: bin
++    name: master
++    type: zip
+diff --git a/ext/pthreads/classes/collectable.h b/ext/pthreads/classes/collectable.h
+new file mode 100644
+index 00000000..b2b09182
+--- /dev/null
++++ b/ext/pthreads/classes/collectable.h
+@@ -0,0 +1,32 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_CLASS_COLLECTABLE_H
++#define HAVE_PTHREADS_CLASS_COLLECTABLE_H
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Collectable_isGarbage, 0, 0, _IS_BOOL, 0)
++ZEND_END_ARG_INFO()
++
++extern zend_function_entry pthreads_collectable_methods[];
++#else
++#	ifndef HAVE_PTHREADS_CLASS_COLLECTABLE
++#	define HAVE_PTHREADS_CLASS_COLLECTABLE
++zend_function_entry pthreads_collectable_methods[] = {
++	PHP_ABSTRACT_ME(Collectable, isGarbage, 	Collectable_isGarbage)
++	{NULL, NULL, NULL}
++};
++#	endif
++#endif
+diff --git a/ext/pthreads/classes/pool.h b/ext/pthreads/classes/pool.h
+new file mode 100644
+index 00000000..6023133c
+--- /dev/null
++++ b/ext/pthreads/classes/pool.h
+@@ -0,0 +1,349 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_CLASS_POOL_H
++#define HAVE_PTHREADS_CLASS_POOL_H
++PHP_METHOD(Pool, __construct);
++PHP_METHOD(Pool, resize);
++PHP_METHOD(Pool, submit);
++PHP_METHOD(Pool, submitTo);
++PHP_METHOD(Pool, collect);
++PHP_METHOD(Pool, shutdown);
++
++ZEND_BEGIN_ARG_INFO_EX(Pool___construct, 0, 0, 1)
++	ZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, class, IS_STRING, 0)
++	ZEND_ARG_TYPE_INFO(0, ctor, IS_ARRAY, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Pool_resize, 0, 0, 1)
++	ZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Pool_submit, 0, 0, 1)
++	ZEND_ARG_OBJ_INFO(0, task, Threaded, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Pool_submitTo, 0, 0, 2)
++	ZEND_ARG_TYPE_INFO(0, worker, IS_LONG, 0)
++	ZEND_ARG_OBJ_INFO(0, task, Threaded, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Pool_collect, 0, 0, 0)
++	ZEND_ARG_CALLABLE_INFO(0, collector, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Pool_noargs, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++extern zend_function_entry pthreads_pool_methods[];
++#else
++#	ifndef HAVE_PTHREADS_CLASS_POOL
++#	define HAVE_PTHREADS_CLASS_POOL
++zend_function_entry pthreads_pool_methods[] = {
++	PHP_ME(Pool, __construct, 	Pool___construct, 	ZEND_ACC_PUBLIC)
++	PHP_ME(Pool, resize, 		Pool_resize, 		ZEND_ACC_PUBLIC)
++	PHP_ME(Pool, submit, 		Pool_submit, 		ZEND_ACC_PUBLIC)
++	PHP_ME(Pool, submitTo, 		Pool_submitTo, 		ZEND_ACC_PUBLIC)
++	PHP_ME(Pool, collect, 		Pool_collect, 		ZEND_ACC_PUBLIC)
++	PHP_ME(Pool, shutdown, 		Pool_noargs, 		ZEND_ACC_PUBLIC)
++	{NULL, NULL, NULL}
++};
++
++/* {{{ proto Pool Pool::__construct(integer size, [class worker, [array $ctor]]) 	
++	Construct a pool ready to create a maximum of $size workers of class $worker
++	$ctor will be used as arguments to constructor when spawning workers */
++PHP_METHOD(Pool, __construct) 
++{
++	zend_long size = 0;
++	zend_class_entry *clazz = NULL;
++	zval *ctor = NULL;
++	
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|Ca", &size, &clazz, &ctor) != SUCCESS) {
++		return;
++	}
++	
++	if (clazz == NULL) clazz = pthreads_worker_entry;
++	
++	if (!instanceof_function(clazz, pthreads_worker_entry)) {
++		zend_throw_exception_ex(NULL, 0, 
++			"The class provided (%s) does not extend Worker", clazz->name->val);
++	}
++	
++	zend_update_property_long(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("size"), size);
++	zend_update_property_stringl(
++		Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("class"), clazz->name->val, clazz->name->len);
++	if (ctor)
++		zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("ctor"), ctor);
++} /* }}} */
++
++/* {{{ proto void Pool::resize(integer size) 
++	Resize the pool to the given number of workers, if the pool size is being reduced 
++	then the last workers started will be shutdown until the pool is the requested size */
++PHP_METHOD(Pool, resize) {
++	zval tmp[2];
++	uint32_t newsize = 0;
++	zval *workers = NULL;
++	zval *size = NULL;
++	
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &newsize) != SUCCESS) {
++		return;
++	}
++	
++	workers = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("workers"), 1, &tmp[0]);
++	size    = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("size"),    1, &tmp[1]);
++	
++	if (Z_TYPE_P(workers) == IS_ARRAY &&
++		newsize < zend_hash_num_elements(Z_ARRVAL_P(workers))) {
++		do {
++			zval *worker = NULL;
++			zend_long top = zend_hash_num_elements(Z_ARRVAL_P(workers));
++			
++			if ((worker = zend_hash_index_find(
++				Z_ARRVAL_P(workers), top-1))) {
++				zend_call_method(
++					worker, Z_OBJCE_P(worker), NULL, ZEND_STRL("shutdown"), NULL, 0, NULL, NULL);
++
++			}
++			
++			zend_hash_index_del(Z_ARRVAL_P(workers), top-1);
++		} while (zend_hash_num_elements(Z_ARRVAL_P(workers)) != newsize);
++	}
++	
++	ZVAL_LONG(size, newsize);
++} /* }}} */
++
++/* {{{ proto integer Pool::submit(Threaded task) 
++	Will submit the given task to the next worker in the pool, by default workers are selected round robin */
++PHP_METHOD(Pool, submit) {
++	zval tmp[5];
++	zval *task = NULL;
++	zval *last = NULL;
++	zval *size = NULL;
++	zval *workers = NULL;
++	zval worker;
++	zval *clazz = NULL;
++	zval *ctor = NULL;
++	zval *selected = NULL;
++	
++	zend_class_entry *ce = NULL;
++	
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &task, pthreads_threaded_entry) != SUCCESS) {
++		return;
++	}
++
++	if (!instanceof_function(Z_OBJCE_P(task), pthreads_threaded_entry)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException,
++			0, "only Threaded objects may be submitted, %s is not Threaded",
++			ZSTR_VAL(Z_OBJCE_P(task)->name));
++		return;
++	}
++	
++	last = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("last"), 1, &tmp[0]);
++	size = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("size"), 1, &tmp[1]);
++	workers = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("workers"), 1, &tmp[2]);
++
++	if (Z_TYPE_P(workers) != IS_ARRAY)
++		array_init(workers);
++	
++	if (Z_LVAL_P(last) >= Z_LVAL_P(size))
++		ZVAL_LONG(last, 0);
++
++	if (!(selected = zend_hash_index_find(Z_ARRVAL_P(workers), Z_LVAL_P(last)))) {
++		clazz = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("class"), 1, &tmp[3]);
++		
++		if (Z_TYPE_P(clazz) != IS_STRING) {
++			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++				"this Pool has not been initialized properly, Worker class not valid");
++			return;
++		}
++		
++		if (!(ce = zend_lookup_class(
++			Z_STR_P(clazz)))) {
++			zend_throw_exception_ex(spl_ce_RuntimeException, 0, 
++				"this Pool has not been initialized properly, the Worker class %s could not be found", 
++				Z_STRVAL_P(clazz));
++			return;
++		}
++		
++		ctor  = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("ctor"), 1, &tmp[4]);
++		
++		object_init_ex(&worker, ce);
++		
++		{
++			zend_class_entry *scope = EG(fake_scope);
++			zend_function *constructor = NULL;
++			zval retval;
++
++			ZVAL_UNDEF(&retval);
++			
++			EG(fake_scope) = ce;
++
++			constructor = Z_OBJ_HT(worker)->get_constructor(Z_OBJ(worker));
++
++			EG(fake_scope) = scope;
++			
++			if (constructor) {
++				zend_fcall_info fci = empty_fcall_info;
++				zend_fcall_info_cache fcc = empty_fcall_info_cache;
++
++				fci.size = sizeof(zend_fcall_info);
++				fci.object = Z_OBJ(worker);
++				fci.retval = &retval;
++				fci.no_separation = 1;
++				
++#if PHP_VERSION_ID < 70300
++				fcc.initialized = 1;
++#endif
++				fcc.function_handler = constructor;
++				fcc.calling_scope = zend_get_executed_scope();
++				fcc.called_scope = Z_OBJCE(worker);
++				fcc.object = Z_OBJ(worker);
++				
++				if (ctor)
++					zend_fcall_info_args(&fci, ctor);
++				
++				zend_call_function(&fci, &fcc);
++				
++				if (ctor)
++					zend_fcall_info_args_clear(&fci, 1);
++				
++				if (Z_TYPE(retval) != IS_UNDEF)
++					zval_dtor(&retval);
++			}
++			
++			zend_call_method(&worker, Z_OBJCE(worker), NULL, ZEND_STRL("start"), NULL, 0, NULL, NULL);
++		}
++		
++		selected = zend_hash_index_update(
++			Z_ARRVAL_P(workers), Z_LVAL_P(last), &worker);
++		
++	}
++	
++	zend_call_method(selected, Z_OBJCE_P(selected), NULL, ZEND_STRL("stack"), NULL, 1, task, NULL);
++	ZVAL_LONG(return_value, Z_LVAL_P(last));
++	Z_LVAL_P(last)++;
++} /* }}} */
++
++/* {{{ proto integer Pool::submitTo(integer $worker, Threaded task) 
++	Will submit the given task to the specified worker */
++PHP_METHOD(Pool, submitTo) {
++	zval tmp;
++	zval *task = NULL;
++	zval *workers = NULL;
++	zend_long worker = 0;
++	zval *selected = NULL;
++	
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lO", &worker, &task, pthreads_threaded_entry) != SUCCESS) {
++		return;
++	}
++	
++	workers = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("workers"), 1, &tmp);
++
++	if (Z_TYPE_P(workers) != IS_ARRAY)
++		array_init(workers);
++	
++	if ((selected = zend_hash_index_find(Z_ARRVAL_P(workers), worker))) {
++		zend_call_method(selected, 
++			Z_OBJCE_P(selected), NULL, 
++			ZEND_STRL("stack"), NULL, 1, task, NULL);
++		ZVAL_LONG(return_value, worker);
++	} else {
++		zend_throw_exception_ex(NULL, 0, 
++			"The selected worker (%ld) does not exist", worker);
++	}
++} /* }}} */
++
++/* {{{ proto void Pool::collect([callable collector])
++	Shall execute the collector on each of the tasks in the working set 
++		removing the task if the collector returns positively
++		the collector should be a function accepting a single task */
++PHP_METHOD(Pool, collect) {
++	zval tmp;
++	pthreads_call_t call;
++	zval *workers = NULL,
++	     *worker = NULL;
++	zend_long collectable = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|f", &call.fci, &call.fcc) != SUCCESS) {
++		return;
++	}
++	
++	workers = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("workers"), 1, &tmp);
++
++	if (Z_TYPE_P(workers) != IS_ARRAY)
++		RETURN_LONG(0);
++
++	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(workers), worker) {
++		pthreads_object_t *thread = 
++			PTHREADS_FETCH_FROM(Z_OBJ_P(worker));
++		if (!ZEND_NUM_ARGS())
++			PTHREADS_WORKER_COLLECTOR_INIT(call, Z_OBJ_P(worker));
++		collectable += pthreads_stack_collect(
++			&thread->std,
++			thread->stack, 
++			&call, 
++			pthreads_worker_running_function, 
++			pthreads_worker_collect_function);
++		if (!ZEND_NUM_ARGS())
++			PTHREADS_WORKER_COLLECTOR_DTOR(call);
++	} ZEND_HASH_FOREACH_END();
++
++	RETURN_LONG(collectable);
++} /* }}} */
++
++/* {{{ */
++static inline int pthreads_pool_shutdown_worker(zval *worker) {
++	zval retval;
++	zend_execute_data *ex = EG(current_execute_data);
++	ZVAL_UNDEF(&retval);
++	EG(current_execute_data) = NULL;
++	zend_call_method_with_0_params(
++		worker, Z_OBJCE_P(worker), NULL, "shutdown", &retval);
++	if (Z_TYPE(retval) != IS_UNDEF)
++		zval_ptr_dtor(&retval);
++	EG(current_execute_data) = ex;
++
++	return ZEND_HASH_APPLY_REMOVE;
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_pool_shutdown(zval *pool) {
++	zval tmp;
++	zval *workers = zend_read_property(
++		Z_OBJCE_P(pool), pool, ZEND_STRL("workers"), 1, &tmp);
++	
++	if (Z_TYPE_P(workers) == IS_ARRAY) {
++		if (zend_hash_num_elements(Z_ARRVAL_P(workers))) {
++			zend_hash_apply(Z_ARRVAL_P(workers), pthreads_pool_shutdown_worker);	
++		}
++
++		zend_hash_clean(Z_ARRVAL_P(workers));
++	}
++} /* }}} */
++
++/* {{{ proto void Pool::shutdown(void)
++	Will cause all the workers to finish executing their stacks and shutdown */
++PHP_METHOD(Pool, shutdown) {
++	if (zend_parse_parameters_none() != SUCCESS) {
++		return;
++	}
++	
++	pthreads_pool_shutdown(getThis());
++} /* }}} */
++#	endif
++#endif
+diff --git a/ext/pthreads/classes/socket.h b/ext/pthreads/classes/socket.h
+new file mode 100644
+index 00000000..1a498ecc
+--- /dev/null
++++ b/ext/pthreads/classes/socket.h
+@@ -0,0 +1,411 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2016                                       |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_CLASS_SOCKET_H
++#define HAVE_PTHREADS_CLASS_SOCKET_H
++PHP_METHOD(Socket, __construct);
++
++PHP_METHOD(Socket, setOption);
++PHP_METHOD(Socket, getOption);
++
++PHP_METHOD(Socket, bind);
++PHP_METHOD(Socket, listen);
++PHP_METHOD(Socket, accept);
++PHP_METHOD(Socket, connect);
++PHP_METHOD(Socket, select);
++
++PHP_METHOD(Socket, read);
++PHP_METHOD(Socket, write);
++PHP_METHOD(Socket, send);
++PHP_METHOD(Socket, recvfrom);
++PHP_METHOD(Socket, sendto);
++
++PHP_METHOD(Socket, setBlocking);
++PHP_METHOD(Socket, getPeerName);
++PHP_METHOD(Socket, getSockName);
++
++PHP_METHOD(Socket, getLastError);
++PHP_METHOD(Socket, clearError);
++PHP_METHOD(Socket, strerror);
++
++PHP_METHOD(Socket, close);
++
++ZEND_BEGIN_ARG_INFO_EX(Socket___construct, 0, 0, 3)
++	ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, protocol, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_setOption, 0, 3, _IS_BOOL, 0)
++	ZEND_ARG_TYPE_INFO(0, level, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, name,  IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_getOption, 0, 2, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, level, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, name,  IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_bind, 0, 1, _IS_BOOL, 0)
++	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
++	ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_listen, 0, 1, _IS_BOOL, 0)
++	ZEND_ARG_TYPE_INFO(0, backlog, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_connect, 0, 1, _IS_BOOL, 0)
++	ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
++	ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_read, 0, 0, 1)
++	ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_write, 0, 0, 1)
++	ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0)
++	ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_send, 0, 0, 3)
++	ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0)
++	ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_recvfrom, 0, 0, 4)
++	ZEND_ARG_TYPE_INFO(1, buffer, IS_STRING, 1)
++	ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(1, name, IS_STRING, 1)
++	ZEND_ARG_TYPE_INFO(1, port, IS_LONG, 1)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_sendto, 0, 0, 4)
++	ZEND_ARG_TYPE_INFO(0, buffer, IS_STRING, 0)
++	ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(0, addr, IS_STRING, 0)
++	ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_setBlocking, 0, 1, _IS_BOOL, 0)
++	ZEND_ARG_TYPE_INFO(0, blocking, _IS_BOOL, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_getHost, 0, 0, IS_ARRAY, 0)
++	ZEND_ARG_TYPE_INFO(0, port, _IS_BOOL, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_select, 0, 0, 4)
++	ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1)
++	ZEND_ARG_TYPE_INFO(1, write, IS_ARRAY, 1)
++	ZEND_ARG_TYPE_INFO(1, except, IS_ARRAY, 1)
++	ZEND_ARG_TYPE_INFO(0, sec, IS_LONG, 1)
++	ZEND_ARG_TYPE_INFO(0, usec, IS_LONG, 0)
++	ZEND_ARG_TYPE_INFO(1, error, IS_LONG, 1)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_accept, 0, 0, 0)
++	ZEND_ARG_INFO(0, class)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_getLastError, 0, 0, 0)
++	ZEND_ARG_TYPE_INFO(0, clear, _IS_BOOL, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Socket_strerror, 0, 1, IS_STRING, 1)
++	ZEND_ARG_TYPE_INFO(0, error, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Socket_void, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++extern zend_function_entry pthreads_socket_methods[];
++#else
++#	ifndef HAVE_PTHREADS_CLASS_SOCKET
++#	define HAVE_PTHREADS_CLASS_SOCKET
++zend_function_entry pthreads_socket_methods[] = {
++	PHP_ME(Socket, __construct, Socket___construct, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, setOption, Socket_setOption, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, getOption, Socket_getOption, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, bind, Socket_bind, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, listen, Socket_listen, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, accept, Socket_accept, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, connect, Socket_connect, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, select, Socket_select, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
++	PHP_ME(Socket, read, Socket_read, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, write, Socket_write, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, send, Socket_send, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, recvfrom, Socket_recvfrom, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, sendto, Socket_sendto, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, setBlocking, Socket_setBlocking, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, getPeerName, Socket_getHost, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, getSockName, Socket_getHost, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, close, Socket_void, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, getLastError, Socket_getLastError, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, clearError, Socket_void, ZEND_ACC_PUBLIC)
++	PHP_ME(Socket, strerror, Socket_strerror, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
++	{NULL, NULL, NULL}
++};
++
++/* {{{ proto Socket::__construct(int domain, int type, int protocol) 
++	Create a Threaded Socket */
++PHP_METHOD(Socket, __construct) {
++	zend_long domain = AF_INET;
++	zend_long type = SOCK_STREAM;
++	zend_long protocol = 0;
++	
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &domain, &type, &protocol) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_construct(getThis(), domain, type, protocol);
++} /* }}} */
++
++/* {{{ proto bool Socket::setOption(int level, int name, int value) 
++	Sets long socket option */
++PHP_METHOD(Socket, setOption) {
++	zend_long level = 0;
++	zend_long name = 0;
++	zend_long value = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &level, &name, &value) != SUCCESS) {
++		RETURN_FALSE;
++	}
++
++	pthreads_socket_set_option(getThis(), level, name, value, return_value);
++} /* }}} */
++
++/* {{{ proto int Socket::getOption(int level, int name)
++	Get long socket option */
++PHP_METHOD(Socket, getOption) {
++	zend_long level = 0;
++	zend_long name = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &level, &name) != SUCCESS) {
++		RETURN_LONG(0);
++	}
++
++	pthreads_socket_get_option(getThis(), level, name, return_value);
++} /* }}} */
++
++/* {{{ proto bool Socket::bind(string host [, int port]) */
++PHP_METHOD(Socket, bind) {
++	zend_string *host = NULL;
++	zend_long port = 0;
++	
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &host, &port) != SUCCESS) {
++		RETURN_FALSE;
++	}
++
++	pthreads_socket_bind(getThis(), host, port, return_value);
++} /* }}} */
++
++/* {{{ proto bool Socket::listen([int backlog = 0]) */
++PHP_METHOD(Socket, listen) {
++	zend_long backlog = 0;
++	
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &backlog) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_listen(getThis(), backlog, return_value);
++} /* }}} */
++
++/* {{{ proto Socket|bool Socket::accept([string class = self::class]) */
++PHP_METHOD(Socket, accept) {
++	zend_class_entry *ce = zend_get_called_scope(execute_data);
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|C", &ce) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_accept(getThis(), ce, return_value);
++} /* }}} */
++
++/* {{{ proto bool Socket::connect(string host[, int port]) */
++PHP_METHOD(Socket, connect) {
++	zend_string *host = NULL;
++	zend_long port = 0;
++	int argc = ZEND_NUM_ARGS();
++
++	if (zend_parse_parameters(argc, "S|l", &host, &port) != SUCCESS) {
++		RETURN_FALSE;
++	}
++
++	pthreads_socket_connect(getThis(), argc, host, port, return_value);
++} /* }}} */
++
++/* {{{ proto int|bool Socket::select(array &read, array &write, array &except, int sec [, int usec = 0 [, int &error]]) */
++PHP_METHOD(Socket, select) {
++	zval *read, *write, *except, *sec, *errorno = NULL;
++	zend_long usec = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/!a/!a/!z!|lz/", &read, &write, &except, &sec, &usec, &errorno) != SUCCESS) {
++		return;
++	}
++	pthreads_socket_select(read, write, except, sec, usec, errorno, return_value);
++} /* }}} */
++
++/* {{{ proto string|bool Socket::read(int length [, int flags = 0 [, int type = Socket::BINARY_READ]]) */
++PHP_METHOD(Socket, read) {
++	zend_long length = 0;
++	zend_long flags = 0;
++	zend_long type = PTHREADS_BINARY_READ;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ll", &length, &flags, &type) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_read(getThis(), length, flags, type, return_value);
++} /* }}} */
++
++/* {{{ proto int|bool Socket::write(string buffer [, int length]) */
++PHP_METHOD(Socket, write) {
++	zend_string *buffer = NULL;
++	zend_long length = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &buffer, &length) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_write(getThis(), buffer, length, return_value);
++} /* }}} */
++
++/* {{{ proto int|bool Socket::send(string buffer, int length, int flags) */
++PHP_METHOD(Socket, send) {
++	zend_string *buffer = NULL;
++	zend_long length = 0;
++	zend_long flags = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sll", &buffer, &length, &flags) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_send(getThis(), buffer, length, flags, return_value);
++} /* }}} */
++
++/* {{{ proto bool Socket::recvfrom(string &buf, int length, int flags, string &name [, int &port ]) */
++PHP_METHOD(Socket, recvfrom) {
++	zval		*buffer, *name, *port = NULL;
++	zend_long	len, flags;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/llz/|z/", &buffer, &len, &flags, &name, &port) == FAILURE) {
++		return;
++	}
++
++	/* overflow check */
++	if ((len + 2) < 3) {
++		RETURN_FALSE;
++	}
++
++	pthreads_socket_recvfrom(getThis(), buffer, len, flags, name, port, return_value);
++} /* }}} */
++
++/* {{{ proto bool Socket::sendto(string buf, int length, int flags, string addr [, int port ]) */
++PHP_METHOD(Socket, sendto) {
++	zend_string *buffer, *address = NULL;
++	zend_long	len, flags, port = 0;
++	int	argc = ZEND_NUM_ARGS();
++
++	if (zend_parse_parameters(argc, "SllS|l", &buffer, &len, &flags, &address, &port) == FAILURE) {
++		return;
++	}
++
++	pthreads_socket_sendto(getThis(), argc, buffer, len, flags, address, port, return_value);
++} /* }}} */
++
++/* {{{ proto bool Socket::setBlocking(bool blocking) */
++PHP_METHOD(Socket, setBlocking) {
++	zend_bool blocking = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "b", &blocking) != SUCCESS) {
++		RETURN_FALSE;
++	}
++
++	pthreads_socket_set_blocking(getThis(), blocking, return_value);
++} /* }}} */
++
++/* {{{ proto array Socket::getPeerName([bool port = true]) */
++PHP_METHOD(Socket, getPeerName) {
++	zend_bool port = 1;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &port) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_get_peer_name(getThis(), port, return_value);
++} /* }}} */
++
++/* {{{ proto array Socket::getSockName([bool port = true]) */
++PHP_METHOD(Socket, getSockName) {
++	zend_bool port = 1;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &port) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_get_sock_name(getThis(), port, return_value);
++} /* }}} */
++
++/* {{{ proto int|bool Socket::getLastError([bool clear = false]) */
++PHP_METHOD(Socket, getLastError) {
++	zend_bool clear = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &clear) != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_get_last_error(getThis(), clear, return_value);
++} /* }}} */
++
++/* {{{ proto void Socket::clearError() */
++PHP_METHOD(Socket, clearError) {
++	if (zend_parse_parameters_none() != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_clear_error(getThis());
++} /* }}} */
++
++/* {{{ proto string|null Socket::strerror(int error) */
++PHP_METHOD(Socket, strerror) {
++	zend_long error = 0;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &error) != SUCCESS) {
++		RETURN_NULL();
++	}
++
++	pthreads_socket_strerror(error, return_value);
++} /* }}} */
++
++/* {{{ proto bool Socket::close(void) */
++PHP_METHOD(Socket, close) {
++	if (zend_parse_parameters_none() != SUCCESS) {
++		return;
++	}
++
++	pthreads_socket_close(getThis(), return_value);
++} /* }}} */
++#	endif
++#endif
+diff --git a/ext/pthreads/classes/thread.h b/ext/pthreads/classes/thread.h
+new file mode 100644
+index 00000000..5e5d1a62
+--- /dev/null
++++ b/ext/pthreads/classes/thread.h
+@@ -0,0 +1,155 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_CLASS_THREAD_H
++#define HAVE_PTHREADS_CLASS_THREAD_H
++PHP_METHOD(Thread, start);
++PHP_METHOD(Thread, join);
++PHP_METHOD(Thread, isStarted);
++PHP_METHOD(Thread, isJoined);
++PHP_METHOD(Thread, getThreadId);
++PHP_METHOD(Thread, getCurrentThreadId);
++PHP_METHOD(Thread, getCurrentThread);
++PHP_METHOD(Thread, getCreatorId);
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_start, 0, 0, 0)
++    ZEND_ARG_TYPE_INFO(0, options, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_run, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_join, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_getThreadId, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_getCurrentThreadId, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_getCurrentThread, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_getCreatorId, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_isStarted, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_isJoined, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_isTerminated, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Thread_merge, 0, 0, 1)
++    ZEND_ARG_INFO(0, from)
++    ZEND_ARG_TYPE_INFO(0, overwrite, _IS_BOOL, 0)
++ZEND_END_ARG_INFO()
++
++extern zend_function_entry pthreads_thread_methods[];
++#else
++#	ifndef HAVE_PTHREADS_CLASS_THREAD
++#	define HAVE_PTHREADS_CLASS_THREAD
++zend_function_entry pthreads_thread_methods[] = {
++	PHP_ME(Thread, start, Thread_start, ZEND_ACC_PUBLIC)
++	PHP_ME(Thread, join, Thread_join, ZEND_ACC_PUBLIC)
++	PHP_ME(Thread, isStarted, Thread_isStarted, ZEND_ACC_PUBLIC)
++	PHP_ME(Thread, isJoined, Thread_isJoined, ZEND_ACC_PUBLIC)
++	PHP_ME(Thread, getThreadId, Thread_getThreadId, ZEND_ACC_PUBLIC)
++	PHP_ME(Thread, getCreatorId, Thread_getThreadId, ZEND_ACC_PUBLIC)
++	PHP_ME(Thread, getCurrentThreadId, Thread_getCurrentThreadId, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
++	PHP_ME(Thread, getCurrentThread, Thread_getCurrentThread, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
++	{NULL, NULL, NULL}
++};
++
++/* {{{ proto boolean Thread::start([long $options = PTHREADS_INHERIT_ALL])
++		Starts executing the implementations run method in a thread, will return a boolean indication of success
++		$options should be a mask of inheritance constants */
++PHP_METHOD(Thread, start)
++{
++	pthreads_object_t* thread = PTHREADS_FETCH;
++	zend_long options = PTHREADS_INHERIT_ALL;
++	
++	if (ZEND_NUM_ARGS()) {
++		if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &options) != SUCCESS) {
++			return;
++		}
++
++		thread->options = options;
++	}
++
++	RETURN_BOOL(pthreads_start(thread));
++} /* }}} */
++
++/* {{{ proto Thread::isStarted() 
++	Will return true if a Thread has been started */
++PHP_METHOD(Thread, isStarted)
++{
++	pthreads_object_t* thread = PTHREADS_FETCH;
++
++	RETURN_BOOL(pthreads_monitor_check(thread->monitor, PTHREADS_MONITOR_STARTED));
++} /* }}} */
++
++/* {{{ proto Thread::isJoined()
++	Will return true if a Thread has been joined already */
++PHP_METHOD(Thread, isJoined)
++{
++	pthreads_object_t* thread = PTHREADS_FETCH;
++
++	RETURN_BOOL(pthreads_monitor_check(thread->monitor, PTHREADS_MONITOR_JOINED));
++} /* }}} */
++
++/* {{{ proto boolean Thread::join() 
++		Will return a boolean indication of success */
++PHP_METHOD(Thread, join) 
++{ 
++	pthreads_object_t* thread = PTHREADS_FETCH;
++
++	RETURN_BOOL(pthreads_join(thread));
++} /* }}} */
++
++/* {{{ proto long Thread::getThreadId()
++	Will return the identifier of the referenced Thread */
++PHP_METHOD(Thread, getThreadId)
++{
++	ZVAL_LONG(return_value, (PTHREADS_FETCH_FROM(Z_OBJ_P(getThis())))->local.id);
++} /* }}} */
++
++/* {{{ proto long Thread::getCurrentThreadId()
++	Will return the identifier of the current Thread */
++PHP_METHOD(Thread, getCurrentThreadId)
++{
++	ZVAL_LONG(return_value, pthreads_self());
++} /* }}} */
++
++/* {{{ proto Thread Thread::getCurrentThread()
++	Will return the currently executing Thread */
++PHP_METHOD(Thread, getCurrentThread)
++{
++	pthreads_current_thread(return_value);
++} /* }}} */
++
++/* {{{ proto long Thread::getCreatorId() 
++	Will return the identifier of the thread ( or process ) that created the referenced Thread */
++PHP_METHOD(Thread, getCreatorId)
++{
++	ZVAL_LONG(return_value, (PTHREADS_FETCH_FROM(Z_OBJ_P(getThis())))->creator.id);
++} /* }}} */
++#	endif
++#endif
+diff --git a/ext/pthreads/classes/threaded.h b/ext/pthreads/classes/threaded.h
+new file mode 100644
+index 00000000..f3dc5c02
+--- /dev/null
++++ b/ext/pthreads/classes/threaded.h
+@@ -0,0 +1,333 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_CLASS_THREADED_H
++#define HAVE_PTHREADS_CLASS_THREADED_H
++PHP_METHOD(Threaded, run);
++PHP_METHOD(Threaded, wait);
++PHP_METHOD(Threaded, notify);
++PHP_METHOD(Threaded, notifyOne);
++PHP_METHOD(Threaded, isRunning);
++PHP_METHOD(Threaded, isTerminated);
++
++PHP_METHOD(Threaded, synchronized);
++PHP_METHOD(Threaded, merge);
++PHP_METHOD(Threaded, shift);
++PHP_METHOD(Threaded, chunk);
++PHP_METHOD(Threaded, pop);
++PHP_METHOD(Threaded, count);
++PHP_METHOD(Threaded, extend);
++PHP_METHOD(Threaded, isGarbage);
++
++PHP_METHOD(Threaded, addRef);
++PHP_METHOD(Threaded, delRef);
++PHP_METHOD(Threaded, getRefCount);
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_run, 0, 0, 0)
++ZEND_END_ARG_INFO()
++ZEND_BEGIN_ARG_INFO_EX(Threaded_wait, 0, 0, 0)
++	ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0)
++ZEND_END_ARG_INFO()
++ZEND_BEGIN_ARG_INFO_EX(Threaded_notify, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_isRunning, 0, 0, 0)
++ZEND_END_ARG_INFO()
++ZEND_BEGIN_ARG_INFO_EX(Threaded_isTerminated, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_synchronized, 0, 0, 1)
++	ZEND_ARG_INFO(0, function)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_merge, 0, 0, 1)
++    ZEND_ARG_INFO(0, from)
++    ZEND_ARG_INFO(0, overwrite)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_shift, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_chunk, 0, 0, 1)
++    ZEND_ARG_INFO(0, size)
++    ZEND_ARG_INFO(0, preserve)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_pop, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_count, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_extend, 0, 0, 1)
++    ZEND_ARG_INFO(0, class)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_addRef, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_delRef, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Threaded_getRefCount, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(Threaded_isGarbage, 0, 0, _IS_BOOL, 0)
++ZEND_END_ARG_INFO()
++
++extern zend_function_entry pthreads_threaded_methods[];
++#else
++#	ifndef HAVE_PTHREADS_CLASS_THREADED
++#	define HAVE_PTHREADS_CLASS_THREADED
++zend_function_entry pthreads_threaded_methods[] = {
++	PHP_ME(Threaded, run, Threaded_run, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, wait, Threaded_wait, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, notify, Threaded_notify, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, notifyOne, Threaded_notify, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, isRunning, Threaded_isRunning, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, isTerminated, Threaded_isTerminated, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, synchronized, Threaded_synchronized, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, merge, Threaded_merge, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, shift, Threaded_shift, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, chunk, Threaded_chunk, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, pop, Threaded_pop, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, count, Threaded_count, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, isGarbage, Threaded_isGarbage, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, addRef, Threaded_addRef, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, delRef, Threaded_delRef, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, getRefCount, Threaded_getRefCount, ZEND_ACC_PUBLIC)
++	PHP_ME(Threaded, extend, Threaded_extend, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
++	{NULL, NULL, NULL}
++};
++
++/* {{{ */
++PHP_METHOD(Threaded, run) {} /* }}} */
++
++/* {{{{ */
++PHP_METHOD(Threaded, addRef) 		{ Z_ADDREF_P(getThis()); }
++PHP_METHOD(Threaded, delRef) 		{ zval_ptr_dtor(getThis()); }
++PHP_METHOD(Threaded, getRefCount) 	{ RETURN_LONG(Z_REFCOUNT_P(getThis())); } /* }}} */
++
++/* {{{ proto boolean Threaded::wait([long timeout]) 
++		Will cause the calling thread to wait for notification from the referenced object
++		When a timeout is used and reached boolean false will return
++		Otherwise returns a boolean indication of success */
++PHP_METHOD(Threaded, wait)
++{
++	pthreads_object_t* threaded = PTHREADS_FETCH;
++	zend_long timeout = 0L;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &timeout)==SUCCESS) {
++		RETURN_BOOL(pthreads_monitor_wait(threaded->monitor, timeout) == SUCCESS);
++	}
++} /* }}} */
++
++/* {{{ proto boolean Threaded::notify()
++		Send notification to everyone waiting on the Threaded
++		Will return a boolean indication of success */
++PHP_METHOD(Threaded, notify)
++{
++	pthreads_object_t* threaded = PTHREADS_FETCH;
++
++	RETURN_BOOL(pthreads_monitor_notify(threaded->monitor) == SUCCESS);
++} /* }}} */
++
++/* {{{ proto boolean Threaded::notifyOne()
++		Send notification to one context waiting on the Threaded
++		Will return a boolean indication of success */
++PHP_METHOD(Threaded, notifyOne)
++{
++	pthreads_object_t* threaded = PTHREADS_FETCH;
++
++	RETURN_BOOL(pthreads_monitor_notify_one(threaded->monitor) == SUCCESS);
++} /* }}} */
++
++/* {{{ proto boolean Threaded::isRunning() 
++	Will return true while the referenced Threaded is being executed by a Worker */
++PHP_METHOD(Threaded, isRunning)
++{
++	pthreads_object_t* threaded = PTHREADS_FETCH;
++	
++	RETURN_BOOL(pthreads_monitor_check(threaded->monitor, PTHREADS_MONITOR_RUNNING));
++} /* }}} */
++
++/* {{{ proto boolean Threaded::isTerminated() 
++	Will return true if the referenced Threaded suffered fatal errors or uncaught exceptions */
++PHP_METHOD(Threaded, isTerminated)
++{
++	pthreads_object_t* threaded = PTHREADS_FETCH;
++	
++	RETURN_BOOL(pthreads_monitor_check(threaded->monitor, PTHREADS_MONITOR_ERROR));
++} /* }}} */
++
++/* {{{ proto void Threaded::synchronized(Callable function, ...)
++	Will synchronize the object, call the function, passing anything after the function as parameters
++	 */
++PHP_METHOD(Threaded, synchronized) 
++{
++	pthreads_call_t call = PTHREADS_CALL_EMPTY;
++	uint argc = 0;
++	zval *argv = NULL;
++	pthreads_object_t* threaded= PTHREADS_FETCH;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "f|+", &call.fci, &call.fcc, &argv, &argc) != SUCCESS) {
++		return;
++	}
++
++	zend_fcall_info_argp(&call.fci, argc, argv);
++
++	call.fci.retval = return_value;
++	call.fci.no_separation = 1;
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		/* synchronize property tables */
++		pthreads_store_sync(getThis());
++
++		zend_try {
++			/* call the closure */
++			zend_call_function(&call.fci, &call.fcc);
++		} zend_catch {
++			ZVAL_UNDEF(return_value);
++		} zend_end_try ();
++
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++
++	zend_fcall_info_args_clear(&call.fci, 1);
++} /* }}} */
++
++/* {{{ proto boolean Threaded::merge(mixed $data, [boolean $overwrite = true])
++	Will merge data with the referenced Threaded */
++PHP_METHOD(Threaded, merge) 
++{
++    zval *from;
++    zend_bool overwrite = 1;
++    
++    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &from, &overwrite) != SUCCESS) {
++        return;
++    }
++    
++	RETURN_BOOL((pthreads_store_merge(getThis(), from, overwrite)==SUCCESS));
++} /* }}} */
++
++/* {{{ proto mixed Threaded::shift()
++	Will shift the first member from the object */
++PHP_METHOD(Threaded, shift) 
++{
++    if (zend_parse_parameters_none() != SUCCESS) {
++        return;
++    }
++    
++    pthreads_store_shift(getThis(), return_value);
++} /* }}} */
++
++/* {{{ proto mixed Threaded::chunk(integer $size [, boolean $preserve = false])
++	Will shift the first member from the object */
++PHP_METHOD(Threaded, chunk) 
++{
++    zend_long size;
++    zend_bool preserve = 0;
++    
++    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &size, &preserve) != SUCCESS) {
++        return;
++    }
++    
++    pthreads_store_chunk(getThis(), size, preserve, return_value);
++} /* }}} */
++
++/* {{{ proto mixed Threaded::pop()
++	Will pop the last member from the object */
++PHP_METHOD(Threaded, pop) 
++{
++    if (zend_parse_parameters_none() != SUCCESS) {
++        return;
++    }
++    
++    pthreads_store_pop(getThis(), return_value);
++} /* }}} */
++
++/* {{{ proto boolean Threaded::count()
++	Will return the size of the properties table */
++PHP_METHOD(Threaded, count)
++{
++    if (zend_parse_parameters_none() != SUCCESS) {
++        return;
++    }
++	
++	ZVAL_LONG(return_value, 0);
++	
++	pthreads_store_count(
++		getThis(), &Z_LVAL_P(return_value));
++} /* }}} */
++
++/* {{{ proto Threaded::isGarbage(void) : bool */
++PHP_METHOD(Threaded, isGarbage) {
++	if (zend_parse_parameters_none() != SUCCESS) {
++		return;
++	}
++	RETURN_TRUE;
++} /* }}} */
++
++/* {{{ proto bool Threaded::extend(string class) */
++PHP_METHOD(Threaded, extend) {
++    zend_class_entry *ce = NULL;
++    zend_bool is_final = 0;
++	zend_class_entry *parent;
++
++    if (zend_parse_parameters(ZEND_NUM_ARGS(), "C", &ce) != SUCCESS) {
++        return;
++    }
++
++#ifdef ZEND_ACC_TRAIT
++    if ((ce->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
++        zend_throw_exception_ex(spl_ce_RuntimeException, 0, 
++            "cannot extend trait %s", ce->name->val);
++        return;
++    }
++#endif
++
++    if (ce->ce_flags & ZEND_ACC_INTERFACE) {
++        zend_throw_exception_ex(spl_ce_RuntimeException, 0, 
++            "cannot extend interface %s", 
++            ce->name->val);
++        return;
++    }
++    
++    if (ce->parent) {
++        zend_throw_exception_ex(spl_ce_RuntimeException, 0, 
++            "cannot extend class %s, it already extends %s", 
++            ce->name->val,
++            ce->parent->name->val);
++        return;
++    }
++    
++    is_final = ce->ce_flags & ZEND_ACC_FINAL;
++
++    if (is_final)
++        ce->ce_flags = ce->ce_flags &~ ZEND_ACC_FINAL;
++
++	parent = zend_get_called_scope(EG(current_execute_data));
++
++	zend_do_inheritance(ce, parent);
++
++    if (is_final)
++        ce->ce_flags |= ZEND_ACC_FINAL;
++
++    RETURN_BOOL(instanceof_function(ce, parent));
++} /* }}} */
++#	endif
++#endif
+diff --git a/ext/pthreads/classes/worker.h b/ext/pthreads/classes/worker.h
+new file mode 100644
+index 00000000..5ea0cb9d
+--- /dev/null
++++ b/ext/pthreads/classes/worker.h
+@@ -0,0 +1,259 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_CLASS_WORKER_H
++#define HAVE_PTHREADS_CLASS_WORKER_H
++PHP_METHOD(Worker, shutdown);
++PHP_METHOD(Worker, isShutdown);
++PHP_METHOD(Worker, stack);
++PHP_METHOD(Worker, unstack);
++PHP_METHOD(Worker, getStacked);
++PHP_METHOD(Worker, collect);
++PHP_METHOD(Worker, collector);
++
++ZEND_BEGIN_ARG_INFO_EX(Worker_shutdown, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Worker_isShutdown, 0, 0, 0)
++ZEND_END_ARG_INFO()
++
++ZEND_BEGIN_ARG_INFO_EX(Worker_stack, 0, 0, 1)
++	ZEND_ARG_OBJ_INFO(0, work, Threaded, 0)
++ZEND_END_ARG_INFO()
++ZEND_BEGIN_ARG_INFO_EX(Worker_unstack, 0, 0, 0)
++ZEND_END_ARG_INFO()
++ZEND_BEGIN_ARG_INFO_EX(Worker_getStacked, 0, 0, 0)
++ZEND_END_ARG_INFO()
++ZEND_BEGIN_ARG_INFO_EX(Worker_collect, 0, 0, 0)
++	ZEND_ARG_CALLABLE_INFO(0, function, 0)
++ZEND_END_ARG_INFO()
++ZEND_BEGIN_ARG_INFO_EX(Worker_collector, 0, 0, 1)
++	ZEND_ARG_OBJ_INFO(0, collectable, Collectable, 0)
++ZEND_END_ARG_INFO()
++
++extern zend_function_entry pthreads_worker_methods[];
++
++#if PHP_VERSION_ID < 70300
++#define PTHREADS_WORKER_COLLECTOR_INIT(call, w) do { \
++	memset(&call, 0, sizeof(pthreads_call_t)); \
++	call.fci.size = sizeof(zend_fcall_info); \
++	ZVAL_STR(&call.fci.function_name, zend_string_init(ZEND_STRL("collector"), 0)); \
++	call.fcc.function_handler = zend_hash_find_ptr(&(w)->ce->function_table, Z_STR(call.fci.function_name)); \
++	call.fci.object = (w); \
++	call.fcc.initialized = 1; \
++	call.fcc.calling_scope = (w)->ce; \
++	call.fcc.called_scope = (w)->ce; \
++	call.fcc.object = (w); \
++} while(0)
++#else
++#define PTHREADS_WORKER_COLLECTOR_INIT(call, w) do { \
++	memset(&call, 0, sizeof(pthreads_call_t)); \
++	call.fci.size = sizeof(zend_fcall_info); \
++	ZVAL_STR(&call.fci.function_name, zend_string_init(ZEND_STRL("collector"), 0)); \
++	call.fcc.function_handler = zend_hash_find_ptr(&(w)->ce->function_table, Z_STR(call.fci.function_name)); \
++	call.fci.object = (w); \
++	call.fcc.calling_scope = (w)->ce; \
++	call.fcc.called_scope = (w)->ce; \
++	call.fcc.object = (w); \
++} while(0)
++#endif
++
++#define PTHREADS_WORKER_COLLECTOR_DTOR(call) zval_ptr_dtor(&call.fci.function_name)
++#else
++#	ifndef HAVE_PTHREADS_CLASS_WORKER
++#	define HAVE_PTHREADS_CLASS_WORKER
++zend_function_entry pthreads_worker_methods[] = {
++	PHP_ME(Worker, shutdown, Worker_shutdown, ZEND_ACC_PUBLIC)
++	PHP_ME(Worker, stack, Worker_stack, ZEND_ACC_PUBLIC)
++	PHP_ME(Worker, unstack, Worker_unstack, ZEND_ACC_PUBLIC)
++	PHP_ME(Worker, getStacked, Worker_getStacked, ZEND_ACC_PUBLIC)
++	PHP_ME(Worker, isShutdown, Worker_isShutdown, ZEND_ACC_PUBLIC)
++	PHP_ME(Worker, collect, Worker_collect, ZEND_ACC_PUBLIC)
++	PHP_ME(Worker, collector, Worker_collector, ZEND_ACC_PUBLIC)
++	{NULL, NULL, NULL}
++};
++
++/* {{{ proto int Worker::stack(Threaded $work)
++	Pushes an item onto the stack, returns the size of stack */
++PHP_METHOD(Worker, stack)
++{
++	pthreads_object_t* thread = PTHREADS_FETCH;
++	zval *work;
++
++	if (!PTHREADS_IN_CREATOR(thread) || PTHREADS_IS_CONNECTION(thread)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException,
++			0, "only the creator of this %s may call stack",
++			thread->std.ce->name->val);
++		return;
++	}
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &work, pthreads_threaded_entry) != SUCCESS) {
++		return;
++	}
++	
++	RETURN_LONG(pthreads_stack_add(thread->stack, work));	
++} /* }}} */
++
++/* {{{ proto Collectable Worker::unstack()
++	Removes the first item from the stack */
++PHP_METHOD(Worker, unstack)
++{
++	pthreads_object_t* thread = PTHREADS_FETCH;
++
++	if (zend_parse_parameters_none() != SUCCESS) {
++		return;
++	}
++
++	if (!PTHREADS_IN_CREATOR(thread) || PTHREADS_IS_CONNECTION(thread)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 
++			0, "only the creator of this %s may call unstack", 
++			thread->std.ce->name->val);
++		return;
++	}
++
++	pthreads_stack_del(thread->stack, return_value);
++}
++
++/* {{{ proto int Worker::getStacked()
++	Returns the current size of the stack */
++PHP_METHOD(Worker, getStacked)
++{
++	pthreads_object_t* thread = PTHREADS_FETCH;
++
++	RETURN_LONG(pthreads_stack_size(thread->stack));
++}
++
++/* {{{ proto Worker::isShutdown()
++	Will return true if the Worker has been shutdown */
++PHP_METHOD(Worker, isShutdown)
++{
++	pthreads_object_t* thread = PTHREADS_FETCH;
++
++	RETURN_BOOL(pthreads_monitor_check(thread->monitor, PTHREADS_MONITOR_JOINED));
++} /* }}} */
++
++/* {{{ proto boolean Worker::shutdown()
++		Will wait for execution of all Stackables to complete before shutting down the Worker */
++PHP_METHOD(Worker, shutdown) 
++{ 
++	pthreads_object_t* thread = PTHREADS_FETCH;
++
++	RETURN_BOOL(pthreads_join(thread));
++} /* }}} */
++
++/* {{{ proto long Worker::getThreadId()
++	Will return the identifier of the referenced Worker */
++PHP_METHOD(Worker, getThreadId)
++{
++	ZVAL_LONG(return_value, (PTHREADS_FETCH_FROM(Z_OBJ_P(getThis())))->local.id);
++} /* }}} */
++
++/* {{{ proto long Worker::getCreatorId() 
++	Will return the identifier of the thread ( or process ) that created the referenced Worker */
++PHP_METHOD(Worker, getCreatorId)
++{
++	ZVAL_LONG(return_value, (PTHREADS_FETCH_FROM(Z_OBJ_P(getThis())))->creator.id);
++} /* }}} */
++
++/* {{{ */
++static zend_bool pthreads_worker_running_function(zend_object *std, zval *value) {
++	pthreads_object_t *worker = PTHREADS_FETCH_FROM(std),
++					  *running = NULL,
++					  *checking = NULL;
++	zend_bool result = 0;
++
++	if (pthreads_monitor_lock(worker->monitor)) {
++		if (*worker->running) {
++			running = PTHREADS_FETCH_FROM(*worker->running);
++			checking = PTHREADS_FETCH_FROM(Z_OBJ_P(value));
++
++			if (running->monitor == checking->monitor)
++				result = 1;
++		}	
++		pthreads_monitor_unlock(worker->monitor);
++	}
++	
++	return result;
++} /* }}} */
++
++/* {{{ */
++static zend_bool pthreads_worker_collect_function(pthreads_call_t *call, zval *collectable) {
++	zval result;
++	zend_bool remove = 0;
++
++	ZVAL_UNDEF(&result);
++
++	call->fci.retval = &result;
++	call->fci.no_separation = 1;
++
++	zend_fcall_info_argn(&call->fci, 1, collectable);
++
++	if (zend_call_function(&call->fci, &call->fcc) != SUCCESS) {
++		return remove;
++	}
++
++	zend_fcall_info_args_clear(&call->fci, 1);
++
++	if (Z_TYPE(result) != IS_UNDEF) {
++		if (zend_is_true(&result)) {
++			remove = 1;
++		}
++		zval_ptr_dtor(&result);
++	}
++
++	return remove;
++} /* }}} */
++
++/* {{{ proto bool Worker::collector(Collectable collectable) */
++PHP_METHOD(Worker, collector) {
++	zval *collectable;
++
++	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &collectable, pthreads_collectable_entry) != SUCCESS) {
++		return;
++	}
++
++	zend_call_method_with_0_params(collectable, Z_OBJCE_P(collectable), NULL, "isgarbage", return_value);
++} /* }}} */
++
++/* {{{ proto int Worker::collect([callable collector]) */
++PHP_METHOD(Worker, collect)
++{
++	pthreads_object_t *thread = PTHREADS_FETCH;
++	pthreads_call_t call = PTHREADS_CALL_EMPTY;	
++
++	if (!ZEND_NUM_ARGS()) {
++		PTHREADS_WORKER_COLLECTOR_INIT(call, Z_OBJ_P(getThis()));
++	} else if (zend_parse_parameters(ZEND_NUM_ARGS(), "f", &call.fci, &call.fcc) != SUCCESS) {
++		return;
++	}
++
++	if (!PTHREADS_IN_CREATOR(thread) || PTHREADS_IS_CONNECTION(thread)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0,	
++			"only the creator of this %s may call collect",
++			thread->std.ce->name->val);
++		return;
++	}
++
++	RETVAL_LONG(pthreads_stack_collect(&thread->std, thread->stack, &call, pthreads_worker_running_function, pthreads_worker_collect_function));
++
++	if (!ZEND_NUM_ARGS()) {
++		PTHREADS_WORKER_COLLECTOR_DTOR(call);
++	}
++}
++#	endif
++#endif
++
+diff --git a/ext/pthreads/config.m4 b/ext/pthreads/config.m4
+new file mode 100644
+index 00000000..9bb63dae
+--- /dev/null
++++ b/ext/pthreads/config.m4
+@@ -0,0 +1,37 @@
++PHP_ARG_ENABLE(pthreads, whether to enable pthreads,
++[  --enable-pthreads          Enable pthreads])
++
++PHP_ARG_WITH(pthreads-sanitize, whether to enable AddressSanitizer for pthreads,
++[  --with-pthreads-sanitize   Enable AddressSanitizer for pthreads], no, no)
++
++PHP_ARG_WITH(pthreads-dmalloc, whether to enable dmalloc for pthreads,
++[  --with-pthreads-dmalloc   Enable dmalloc for pthreads], no, no)
++
++if test "$PHP_PTHREADS" != "no"; then
++	AC_MSG_CHECKING([for ZTS])   
++	if test "$PHP_THREAD_SAFETY" != "no"; then
++		AC_MSG_RESULT([ok])
++	else
++		AC_MSG_ERROR([pthreads requires ZTS, please re-compile PHP with ZTS enabled])
++	fi
++
++	AC_DEFINE(HAVE_PTHREADS, 1, [Whether you have pthreads support])
++
++	if test "$PHP_PTHREADS_SANITIZE" != "no"; then
++		EXTRA_LDFLAGS="-lasan"
++		EXTRA_CFLAGS="-fsanitize=address -fno-omit-frame-pointer"
++	fi
++	
++	if test "$PHP_PTHREADS_DMALLOC" != "no"; then
++		EXTRA_LDFLAGS="$EXTRA_LDFLAGS -ldmalloc"
++		EXTRA_CFLAGS="$EXTRA_CFLAGS -DDMALLOC"
++	fi
++
++	PHP_NEW_EXTENSION(pthreads, php_pthreads.c src/monitor.c src/stack.c src/globals.c src/prepare.c src/store.c src/resources.c src/handlers.c src/object.c src/socket.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
++	PHP_ADD_BUILD_DIR($ext_builddir/src, 1)
++	PHP_ADD_INCLUDE($ext_builddir)
++	PHP_SUBST(PTHREADS_SHARED_LIBADD)
++    PHP_SUBST(EXTRA_LDFLAGS)
++    PHP_SUBST(EXTRA_CFLAGS)
++	PHP_ADD_MAKEFILE_FRAGMENT
++fi
+diff --git a/ext/pthreads/config.w32 b/ext/pthreads/config.w32
+new file mode 100644
+index 00000000..b808f3e1
+--- /dev/null
++++ b/ext/pthreads/config.w32
+@@ -0,0 +1,25 @@
++var PTHREADS_EXT_NAME="pthreads";
++var PTHREADS_EXT_SRC="monitor.c stack.c globals.c prepare.c store.c resources.c handlers.c object.c socket.c";
++var PTHREADS_EXT_DIR=configure_module_dirname + "/src";
++var PTHREADS_EXT_API="php_pthreads.c";
++var PTHREADS_EXT_FLAGS="/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /I" + configure_module_dirname;
++var PTHREADS_EXT_DEP="pthreadVC2.lib";
++/* --------------------------------------------------------------------- */
++ARG_WITH("pthreads", "for pthreads support", "no");
++
++if (PHP_PTHREADS != "no") {
++	if(CHECK_HEADER_ADD_INCLUDE("pthread.h", "CFLAGS_PTHREADS", PHP_PTHREADS + ";" + configure_module_dirname) &&    
++		CHECK_HEADER_ADD_INCLUDE("sched.h", "CFLAGS_PTHREADS", PHP_PTHREADS + ";" + configure_module_dirname) &&
++		CHECK_LIB(PTHREADS_EXT_DEP, PTHREADS_EXT_NAME, PHP_PTHREADS) &&
++		CHECK_LIB("ws2_32.lib", PTHREADS_EXT_NAME, PHP_PTHREADS) &&
++		CHECK_LIB("Iphlpapi.lib", PTHREADS_EXT_NAME, PHP_PTHREADS)) {
++		EXTENSION(PTHREADS_EXT_NAME, PTHREADS_EXT_API, PHP_PTHREADS_SHARED, PTHREADS_EXT_FLAGS);
++		ADD_SOURCES(
++			PTHREADS_EXT_DIR,
++			PTHREADS_EXT_SRC, 
++			PTHREADS_EXT_NAME
++		);
++	} else {
++		WARNING("pthreads not enabled; libraries and headers not found");
++	}
++}
+diff --git a/ext/pthreads/examples/Benchmark.php b/ext/pthreads/examples/Benchmark.php
+new file mode 100644
+index 00000000..5335db5c
+--- /dev/null
++++ b/ext/pthreads/examples/Benchmark.php
+@@ -0,0 +1,35 @@
++<?php
++/**
++* This file serves as a benchmark for pthreads initialization/destruction
++* usage: php-zts examples/Benchmark.php [threads] [samples]
++*   threads - the number of threads to create, default=100
++*   samples - the number of times to run the test, default=5
++*/
++
++/**
++* Nothing
++*/
++
++$max = @$argv[1] ? $argv[1] : 100;
++$sample = @$argv[2] ? $argv[2] : 5;
++
++printf("Start(%d) ...", $max);
++$it = 0;
++do {
++    $s = microtime(true);
++    /* begin test */
++    $ts = [];
++    while (count($ts)<$max) {
++        $t = new Thread();
++        $t->start();
++        $ts[]=$t;
++    }
++    $ts = [];
++    /* end test */
++    
++    $ti [] = $max/(microtime(true)-$s);
++    printf(".");
++} while ($it++ < $sample);
++
++printf(" %.3f tps\n", array_sum($ti) / count($ti));
++?>
+diff --git a/ext/pthreads/examples/CallAnyFunction.php b/ext/pthreads/examples/CallAnyFunction.php
+new file mode 100644
+index 00000000..0b347287
+--- /dev/null
++++ b/ext/pthreads/examples/CallAnyFunction.php
+@@ -0,0 +1,73 @@
++<?php
++/*
++	This isn't built in as it's a pretty simple task to achieve for your self
++	I can't really think of any functions that are labourious enough that you
++	would want to execute them by themselves in a thread of their own. Maybe
++	you have functions within your own code that could be called in parallel without
++	refactoring for multi-threading capabilities ...
++
++	I personally think you shouldn't try to make chocolate from cheese and you
++	would be better of refactoring your code to be multi-threaded ...
++
++	But here's an example of how you would achieve such a task:
++*/
++class Caller extends Thread {
++	/**
++	* Provide a passthrough to call_user_func_array
++	**/
++	public function __construct(callable $method, ...$params){
++		$this->method = $method;
++		$this->params = $params;
++		$this->result = null;
++		$this->joined = false;
++	}
++
++	/**
++	* The smallest thread in the world
++	**/
++	public function run() {
++		$this->result = 
++			($this->method)(...$this->params); /* gotta love php7 :) */
++	}
++
++	/**
++	* Static method to create your threads from functions ...
++	**/
++	public static function call($method, ...$params){
++		$thread = new Caller($method, ...$params);
++		if($thread->start()){
++			return $thread;
++		}
++	}
++
++	/**
++	* Do whatever, result stored in $this->result, don't try to join twice
++	**/
++	public function __toString(){
++		if(!$this->joined) {
++			$this->joined = true;
++			$this->join();
++		}
++
++		return $this->result;
++	}
++
++
++	private $method;
++	private $params;
++	private $result;
++	private $joined;
++}
++
++/* here's us calling file_get_contents in a thread of it's own */
++$future = Caller::call("file_get_contents", "http://www.php.net");
++/* here's us counting the bytes out, note, __toString() magic joined so no need to join explicitly */
++printf("Got %d bytes from php.net\n", strlen((string)$future));
++/* you can reference again as a string because you cached the result, YOU CANNOT JOIN TWICE */
++printf("First 16 chars: %s\n", substr((string)$future, 0, 16));
++
++/* here's us calling a closure in a thread of it's own */
++$future = Caller::call(function(...$params) {
++	printf("and how about this: %s, %s %s %s %s!\n", ...$params);
++}, "also", "you", "can", "use", "closures");
++?>
+diff --git a/ext/pthreads/examples/ChannelsAndMagic.php b/ext/pthreads/examples/ChannelsAndMagic.php
+new file mode 100644
+index 00000000..e292d983
+--- /dev/null
++++ b/ext/pthreads/examples/ChannelsAndMagic.php
+@@ -0,0 +1,57 @@
++<?php
++/*
++ * This example demonstrates the use of magic methods, and is a possible answer to the question:
++ *   "Can we have something like go channels with pthreads?"
++ * Channels in go abstract away the complexity of parallel programming, but are dependant on goroutines
++ *   and language level support.
++ *  We can't have language level support easily, but we can implement channels using magic PHP.
++ */
++
++class Channel extends Threaded {
++    /* setting a value on the channel shall cause waiters to wake up */
++    final public function __set($key, $value) {
++        return $this->synchronized(function() use ($key, $value) {
++            $this[$key] = $value;
++            return $this->notify();
++        });
++    }
++    
++    /* getting a value on the channel shall cause callers to wait until it's available */
++    final public function __get($key) {
++        return $this->synchronized(function() use($key) {
++            while (!isset($this[$key]))
++                $this->wait();
++            return $this[$key];
++        });
++    }
++}
++
++class Routine extends Threaded {
++    public function __construct(Channel $channel) {
++        $this->channel = $channel;
++    }
++    
++    public function run() {
++        /* sending on the channel */
++        $this
++            ->channel["message"] = "Hello World";
++        $this
++            ->channel["gold"] = 3.462;
++    }
++
++    protected $channel;
++}
++
++$channel = new Channel();
++$pool = new Pool(4);
++
++$pool->submit(
++    new Routine($channel));
++
++/* recving on the channel */
++printf("Message: %s, Gold: %.3f\n", 
++    $channel["message"], 
++    $channel["gold"]);
++
++$pool->shutdown();
++?>
+diff --git a/ext/pthreads/examples/ClosureFuture.php b/ext/pthreads/examples/ClosureFuture.php
+new file mode 100644
+index 00000000..a1533f5d
+--- /dev/null
++++ b/ext/pthreads/examples/ClosureFuture.php
+@@ -0,0 +1,60 @@
++<?php
++/**
++* A synchronized Future for a Closure
++*
++*	This example takes a Closure and executes it in parallel, storing the result
++*	and fetching the result are synchronized operations
++*
++*	This means that a call to getResult() will block the calling context until a result
++*	is available
++**/
++class Future extends Thread {
++
++    private function __construct(Closure $closure, array $args = []) {
++        $this->closure = $closure;
++        $this->args    = $args;	
++    }
++
++    public function run() {
++        $this->synchronized(function() {
++            $this->result = 
++               (array) ($this->closure)(...$this->args);
++            $this->notify();
++        });
++    }
++
++    public function getResult() {
++        return $this->synchronized(function(){
++            while (!$this->result)
++                $this->wait();
++            return $this->result;
++        });
++    }
++    
++    public static function of(Closure $closure, array $args = []) {
++        $future = 
++            new self($closure, $args);
++        $future->start();
++        return $future;
++    }
++    
++	protected $owner;
++    protected $closure;
++    protected $args;
++    protected $result;
++}
++
++/* some data */
++$test = ["Hello", "World"];
++
++/* a closure to execute in background and foreground */
++$closure = function($test) {
++    return $test;
++};
++
++/* make call in background thread */
++$future = Future::of($closure, [$test]);
++
++/* get result of background and foreground call */
++var_dump($future->getResult(), $closure($test));
++?>
+diff --git a/ext/pthreads/examples/KeepAliveSession.php b/ext/pthreads/examples/KeepAliveSession.php
+new file mode 100644
+index 00000000..c122b48f
+--- /dev/null
++++ b/ext/pthreads/examples/KeepAliveSession.php
+@@ -0,0 +1,194 @@
++<?php
++
++/**
++ * This is small example that shows the implementation of a thread based HTTP server
++ * supporting Connection: keep-alive and how session handling can be implemented.
++ * 
++ * To start the server, open the command line and enter:
++ * 
++ *     $ php -f keep-alive-session.php 8822
++ *     
++ * where 8822 is an example port to let the server listen to. To run this example you
++ * need a thread-safe compiled PHP > 5.3 with pthreads enabled.
++ * 
++ * @author Tim Wagner <tw@appserver.io>
++ * @version 0.1.0
++ * @link https://github.com/krakjoe/pthreads
++ */
++class Test extends Thread
++{
++
++    /**
++     * Socket resource to read/write from/to.
++     * 
++     * @var resource
++     */
++    protected $socket;
++
++    /**
++     * Initializes the thread with the socket resource
++     * necessary for reading/writing.
++     * 
++     * @param resource $socket The socket resource
++     * @return void
++     */
++    public function __construct($socket)
++    {
++        $this->socket = $socket;
++    }
++    
++    /**
++     * Found on php.net {@link http://pa1.php.net/function.http-parse-headers#111226}, thanks
++     * to anonymous!
++     * 
++     * @param string $header The header to parse
++     * @return array The headers parsed from the passed string
++     * @see http://pa1.php.net/function.http-parse-headers#111226
++     */
++    public function http_parse_headers($header)
++    {
++        $retVal = array();
++        $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
++        foreach( $fields as $field ) {
++            if (preg_match('/([^:]+): (.+)/m', $field, $match)) {
++                $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
++                if( isset($retVal[$match[1]]) ) {
++                    if (!is_array($retVal[$match[1]])) {
++                        $retVal[$match[1]] = array($retVal[$match[1]]);
++                    }
++                    $retVal[$match[1]][] = $match[2];
++                } else {
++                    $retVal[$match[1]] = trim($match[2]);
++                }
++            }
++        }
++        return $retVal;
++    }
++
++    /**
++     * The thread's run() method that runs in parallel.
++     * 
++     * @link http://www.php.net/manual/en/thread.run.php
++     */
++    public function run()
++    {
++                
++        // initialize the local variables and the socket
++        $threadId = $this->getThreadId();
++
++        $counter = 1;
++        $connectionOpen = true;
++        $startTime = time();
++        
++        $timeout = 5;
++        $maxRequests = 5;
++        
++        $client = socket_accept($this->socket);
++        socket_set_option($client, SOL_SOCKET, SO_RCVTIMEO, array("sec" => $timeout, "usec" => 0));
++        
++        do {
++            
++            // we only read headers here, because it's an example
++            $buffer = '';
++            while ($buffer .= socket_read($client, 1024)) {
++                if (false !== strpos($buffer, "\r\n\r\n")) {
++                    break;
++                }
++            }
++            
++            // check if the clients stopped sending data
++            if ($buffer === '') {
++                
++                socket_close($client);
++                $connectionOpen = false;
++                
++            } else {
++            
++                // parse the request headers
++                $requestHeaders = $this->http_parse_headers($buffer);
++                
++                // simulate $_COOKIE array
++                $_COOKIE = array();
++                if (array_key_exists('Cookie', $requestHeaders)) {
++                    $cookies = explode('; ', $requestHeaders['Cookie']);
++                    foreach ($cookies as $cookie) {
++                        list ($key, $value) = explode('=', $cookie);
++                        $_COOKIE[$key] = $value;
++                    }
++                }
++                
++                // calculate the number of available requests (after this one)
++                $availableRequests = $maxRequests - $counter++;
++    
++                // prepare response headers
++                $headers = array();
++                $headers[] = "HTTP/1.1 200 OK";
++                $headers[] = "Content-Type: text/html";
++                
++                // start the session if not already done 
++                if (session_status() == PHP_SESSION_NONE) {
++                    session_start();
++                }
++                
++                // write data to a REAL PHP session, started with session_start()!
++                $_SESSION["thread_$threadId"]['availableRequest'] = $availableRequests;
++                
++                // add a header to create session cookie
++                $headers[] = "Set-Cookie: " . session_name() . "=" . session_id() . "; Path=/";
++                
++                // prepare HTML body
++                $body = '<html><head><title>A Title</title></head><body><p>Generated by thread: ' . $threadId . '</p><p>' . var_export($_SESSION, true) . '</p></body></html>';
++                
++                // prepare header with content-length
++                $contentLength = strlen($body);
++                $headers[] = "Content-Length: $contentLength";
++    
++                // check if this will be the last requests handled by this thread
++                if ($availableRequests > 0) {
++                    $headers[] = "Connection: keep-alive";
++                    $headers[] = "Keep-Alive: max=$availableRequests, timeout=$timeout, thread={$this->getThreadId()}";
++                } else {
++                    $headers[] = "Connection: close";
++                }
++                
++                // prepare the response head/body
++                $response = array(
++                    "head" => implode("\r\n", $headers) . "\r\n",
++                    "body" => $body
++                );
++                
++                // write the result back to the socket
++                socket_write($client, implode("\r\n", $response));
++                
++                // check if this is the last request
++                if ($availableRequests <= 0) {
++                    // if yes, close the socket and end the do/while
++                    socket_close($client);
++                    $connectionOpen = false;
++                }
++            }
++            
++        } while ($connectionOpen);
++    }        
++}
++
++// intialize the threads and the socket
++$workers = array();
++$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
++socket_bind($socket, '0.0.0.0', $argv[1]);
++socket_listen($socket);
++
++// we start 5 worker threads here
++if ($socket) {
++    
++    $worker = 0;
++    
++    while (++ $worker < 5) {
++        $workers[$worker] = new Test($socket);
++        $workers[$worker]->start(PTHREADS_INHERIT_ALL|PTHREADS_ALLOW_HEADERS);
++    }
++    
++    foreach ($workers as $worker) {
++        $worker->join();
++    }
++}
+diff --git a/ext/pthreads/examples/MySQLi.php b/ext/pthreads/examples/MySQLi.php
+new file mode 100644
+index 00000000..761b6aad
+--- /dev/null
++++ b/ext/pthreads/examples/MySQLi.php
+@@ -0,0 +1,82 @@
++<?php
++/**
++* This example illustrates best practice with regard to using MySQLi in multiple threads
++*
++* For convenience and simplicity it uses a Pool.
++**/
++
++class Connect extends Worker {
++
++    public function __construct($hostname, $username, $password, $database, $port = 3306) {
++        $this->hostname = $hostname;
++        $this->username = $username;
++        $this->password = $password;
++        $this->database = $database;
++        $this->port     = $port;
++    }
++    
++    public function getConnection() {
++        if (!self::$link) {
++            self::$link = new mysqli(
++                $this->hostname, 
++                $this->username, 
++                $this->password, 
++                $this->database, 
++                $this->port);
++        }
++        
++        /* do some exception/error stuff here maybe */       
++         
++        return self::$link;
++    }
++    
++    protected $hostname;
++    protected $username;
++    protected $password;
++    protected $database;
++    protected $port;
++    
++    /**
++    * Note that the link is stored statically, which for pthreads, means thread local
++    **/
++    protected static $link;
++}
++
++class Query extends Threaded {
++
++    public function __construct(string $sql, Threaded $store) {
++        $this->sql = $sql;
++        $this->result = $store;
++    }
++    
++    public function run() {
++        $mysqli = $this->worker->getConnection();
++        
++        $result = $mysqli->query($this->sql);
++        
++        if ($result) {    
++            while (($row = $result->fetch_assoc())) {
++                $this->result[] = "{$row['Id']}) {$row['User']} ({$row['State']})";
++            }
++        }
++    }
++    
++    protected $sql;
++    protected $result;
++}
++
++$pool = new Pool(4, "Connect", ["localhost", "root", "", "mysql"]);
++$stores = [];
++
++for ($i = 0; $i < 6; ++$i) {
++    $store = new Threaded(); // store all results in here for the Query object
++    $pool->submit(new Query("SHOW PROCESSLIST;", $store));
++    $stores[] = $store; // maintain a list of stores to retrieve their results later
++}
++
++$pool->collect(); // collect all finished work to free up memory
++$pool->shutdown(); // shutdown the pool to make sure it has completely finished executing
++
++print_r($stores); // output all results for all Query objects
++
++?>
+diff --git a/ext/pthreads/examples/NonBlockingSocketServer.php b/ext/pthreads/examples/NonBlockingSocketServer.php
+new file mode 100644
+index 00000000..ca87075a
+--- /dev/null
++++ b/ext/pthreads/examples/NonBlockingSocketServer.php
+@@ -0,0 +1,129 @@
++<?php
++class SocketServer extends Thread {
++	public $socket;
++	public $maxThreads;
++	public $isRunning;
++	public $threads;
++	public $host;
++	public $port;
++	public $backlog;
++	public function __construct(int $maxThreads, string $host, int $port, int $backlog) {
++		$this->maxThreads = $maxThreads;
++		$this->isRunning = false;
++		$this->threads = [ ];
++		$this->host = $host;
++		$this->port = $port;
++		$this->backlog = $backlog;
++	}
++	public function run() {
++		$this->socket = new Socket ( AF_INET, SOCK_STREAM, SOL_TCP );
++		
++		$this->socket->setOption ( SOL_SOCKET, SO_REUSEADDR, 1 );
++		
++		$this->socket->bind ( $this->host, $this->port );
++		
++		$this->socket->listen ( $this->backlog );
++		
++		$this->socket->setBlocking ( false );
++		
++		$this->isRunning = true;
++		
++		for($i = 0; $i < $this->maxThreads; $i ++) {
++			$this->threads [$i] = new RequestHandler ( $this->socket );
++			$this->threads [$i]->start ();
++		}
++		
++		$this->synchronized ( function () {
++			do {
++				foreach ( $this->threads as $key => $thread ) {
++					if ($thread->isRunning ()) {
++						continue;
++					}
++					$thread->join ();
++					$this->threads [$key] = new RequestHandler ( $this->socket );
++					$this->threads [$key]->start ();
++				}
++				$this->wait ( 500000 ); // 500 ms
++			} while ( $this->isRunning );
++		} );
++	}
++	public function shutdown() {
++		$this->isRunning = false;
++		foreach ( $this->threads as $thread ) {
++			$thread->shutdown ();
++		}
++		$this->socket->close ();
++	}
++}
++class RequestHandler extends Thread {
++	public $mainSocket;
++	public $threadSocket;
++	public $isRunning;
++	public function __construct(Socket $socket) {
++		$this->mainSocket = $socket;
++		$this->isRunning = false;
++	}
++	public function run() {
++		$this->isRunning = true;
++		
++		while ( $this->isRunning ) {
++			
++			$this->threadSocket = $this->mainSocket->accept ();
++			
++			if ($this->threadSocket === false) {
++				$this->synchronized ( function () {
++					$this->wait ( 5000 ); // 5ms
++				} );
++				continue;
++			}
++			
++			$threadId = $this->getThreadId ();
++			
++			$response = "Welcome\nYou are connected with Thread-ID $threadId\nEnter \"quit\" to quit\n\n";
++			
++			$this->threadSocket->write ( $response, strlen ( $response ) );
++			
++			do {
++				$buffer = trim ( $this->threadSocket->read ( 1024 ) );
++				
++				if ('quit' === $buffer) {
++					break;
++				}
++				
++				$talkBack = "You entered $buffer.\n";
++				
++				$this->threadSocket->write ( $talkBack, strlen ( $talkBack ) );
++			} while ( $this->isRunning );
++			
++			$this->threadSocket->write ( "Goodbye\n", 9 );
++			$this->threadSocket->close ();
++			
++			$this->isRunning = false;
++		}
++	}
++	public function shutdown() {
++		$this->isRunning = false;
++	}
++}
++
++echo 'Multi-Threaded Socket Server started with PID ' . posix_getpid () . "\n";
++
++$server = new SocketServer ( 3, '127.0.0.1', 9004, 2 );
++$server->start ();
++
++$running = true;
++
++$shutdown = function () use (&$running, $server) {
++	echo "Shutting down... \n";
++	$running = false;
++	$server->shutdown ();
++	$server->join ();
++	echo "Finished\n";
++};
++
++pcntl_signal ( SIGINT, $shutdown );
++
++while ( $running ) {
++	pcntl_signal_dispatch ();
++	usleep ( 10000 );
++}
+diff --git a/ext/pthreads/examples/ObjectsAsParameters.php b/ext/pthreads/examples/ObjectsAsParameters.php
+new file mode 100644
+index 00000000..75ea2b01
+--- /dev/null
++++ b/ext/pthreads/examples/ObjectsAsParameters.php
+@@ -0,0 +1,101 @@
++<?php
++/*
++* Because we plan for two contexts to manipulate this object we extend the Threaded declaration
++* This connects automatically the reference in the creating context and the reference in the threading context
++*/
++class Response extends Threaded {
++	
++	public function __construct($url){
++		$this->url = $url;
++		$this->start = 0;
++		$this->finish = 0;
++		$this->data = null;
++		$this->length = 0;
++	}
++	
++	public function setStart($start)	{ $this->start = $start; }
++	public function setFinish($finish)	{ $this->finish = $finish; }
++	public function setData($data)		{ 
++		$this->data = $data; 
++		$this->length = strlen($this->data);
++	}
++	
++	public function getUrl()		{ return $this->url; }
++	public function getDuration()	{ return $this->finish - $this->start; }
++	public function getLength()		{ return $this->length; }
++	public function getData()		{ return $this->data; }
++	public function getStart()		{ return $this->start; }
++	public function getFinish()		{ return $this->finish; }
++	
++	public function run(){
++		/* this particular object won't run */
++	}
++	
++}
++
++class Request extends Thread {
++	/*
++	* You could have more parameters but an object can hold everything ...
++	*/
++	public function __construct($response){
++		$this->response = $response;
++	}
++	
++	/*
++	* Populate the response object for creating context
++	*/
++	public function run(){
++		
++		/*
++		* NOTE:
++		* Referencing threaded objects inside a thread:
++		*	If you plan to heavily manipulate any object then retain a reference in the method scope
++		*	Anytime you read and write the object context of a threaded object locking occurs
++		*	Retaining method scope references avoids some
++		*	$response retains the connection to the reference in the thread that created the request
++		* If you have a threaded member that you only plan to execute ONE time:
++		*	(isWaiting/isRunning for example) then it is acceptable to reference the member via the object context
++		* Referencing the object context on every call will have no adverse affects ( no leaks/errors )
++		* The hints above are best practices but not required.
++		*/
++		if($this->response->getUrl()){
++			$this->response->setStart(microtime(true));
++			$this->response->setData(file_get_contents($this->response->getUrl()));
++			$this->response->setFinish(microtime(true));
++		}
++	}
++}
++
++/*
++* We can retain the reference to the response object !!
++*/
++$response = new Response(
++	sprintf("http://www.google.com/?q=%s", md5(rand()*time()))
++);
++
++/*
++* Initialize a new threaded request with a response object as the only parameter
++*/
++$request = new Request($response);
++
++/*
++* Tell you all about it ...
++*/
++printf("Fetching: %s ", $response->getUrl());
++if($request->start()){
++	/* do something during runtime */
++	while($request->isRunning()) {
++		echo ".";
++		$request->synchronized(function() use($request) {
++			$request->wait(100);
++		});
++	}
++	/* 
++		you do not need to join:
++			when a thread returns false for isRunning then your code is no longer being executed
++			pthreads will cleanup the variable when it goes out of scope like any other variable in php
++	*/
++	printf(" got %d bytes in %f seconds\n", $response->getLength(), $response->getDuration());
++}
++$request->join();
++?>
+diff --git a/ext/pthreads/examples/Pooling.php b/ext/pthreads/examples/Pooling.php
+new file mode 100644
+index 00000000..e05d557b
+--- /dev/null
++++ b/ext/pthreads/examples/Pooling.php
+@@ -0,0 +1,115 @@
++<?php
++class WebWorker extends Worker {
++
++	/*
++	* We accept a SafeLog which is shared among Workers and an array
++	* containing PDO connection parameters
++	*
++	* The PDO connection itself cannot be shared among contexts so
++	* is declared static (thread-local), so that each Worker has it's
++	* own PDO connection.
++	*/
++	public function __construct(SafeLog $logger, array $config = []) {
++		$this->logger = $logger;
++		$this->config = $config;
++	}
++
++	/*
++	* The only thing to do here is setup the PDO object
++	*/
++	public function run() {
++		if (isset($this->config)) {
++			self::$connection = new PDO(... $this->config);
++		}
++	}
++
++	public function getLogger()     { return $this->logger; }
++	public function getConnection() { return self::$connection; }
++	
++	private $logger;
++	private $config;
++	private static $connection;
++}
++
++class WebWork extends Threaded {
++	/*
++	* An example of some work that depends upon a shared logger
++	* and a thread-local PDO connection
++	*/
++	public function run() {
++		$logger = $this->worker->getLogger();
++		$logger->log("%s executing in Thread #%lu", 
++			__CLASS__, $this->worker->getThreadId());
++
++		if ($this->worker->getConnection()) {
++			$logger->log("%s has PDO in Thread #%lu", 
++				__CLASS__, $this->worker->getThreadId());
++		}
++	}
++}
++
++class SafeLog extends Threaded {
++	
++	/*
++	* If logging were allowed to occur without synchronizing
++	*	the output would be illegible
++	*/
++	public function log($message, ... $args) {
++		$this->synchronized(function($message, ... $args){
++			if (is_callable($message)) {
++				$message(...$args);
++			} else echo vsprintf("{$message}\n", ... $args);
++		}, $message, $args);
++	}
++}
++
++$logger = new SafeLog();
++
++/*
++* Constructing the Pool does not create any Threads
++*/
++$pool = new Pool(8, 'WebWorker', [$logger, ["sqlite:example.db"]]);
++
++/*
++* Only when there is work to do are threads created
++*/
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++$pool->submit(new WebWork());
++
++/*
++* The Workers in the Pool retain references to the WebWork objects submitted
++* in order to release that memory the Pool::collect method must be invoked in the same
++* context that created the Pool.
++*
++* The Worker::collect method is invoked for every Worker in the Pool, the garbage list
++* for each Worker is traversed and each piece of work (a WebWork object, in this case)
++* is passed to the optionally provided Closure.
++*
++* Collecting in a continuous loop will cause the garbage list to be emptied.
++*/
++while ($pool->collect());
++
++/*
++* We could submit more stuff here, the Pool is still waiting for work to be submitted.
++*/
++$logger->log(function($pool) {
++	var_dump($pool);
++}, $pool);
++
++/*
++* Shutdown Pools at the appropriate time, don't leave it to chance !
++*/
++$pool->shutdown();
++?>
+diff --git a/ext/pthreads/examples/SelectiveInheritance.php b/ext/pthreads/examples/SelectiveInheritance.php
+new file mode 100644
+index 00000000..2e896754
+--- /dev/null
++++ b/ext/pthreads/examples/SelectiveInheritance.php
+@@ -0,0 +1,72 @@
++<?php
++/*
++* This is for advanced users ONLY !!
++*
++* In a large application, the overhead of each thread having to copy the entire context may become undesireable.
++* Selective Inheritance serves as a way to choose which parts of the environment are available in threading contexts
++* Following is some code that demonstrates the use of this feature
++*
++* Note: if a member of a pthreads object, is an object itself of a user defined type, and the class table is not inherited
++*   you are asking for trouble !!
++* Note: the included_files table is only populated where PTHREADS_INHERIT_INCLUDES is set
++*/
++
++class my_class {}
++
++function my_function(){
++    return __FUNCTION__;
++}
++
++define ("my_constant", 1);
++
++class Selective extends Thread {
++    public function run() {
++        /* functions exist where PTHREADS_INHERIT_FUNCTIONS is set */
++        var_dump(function_exists("my_function"));
++        /* classes exist where PTHREADS_INHERIT_CLASSES is set **BE CAREFUL** */
++        var_dump(class_exists("my_class"));
++        /* constants exist where PTHREADS_INHERIT_CONSTANTS is set */
++        var_dump(defined("my_constant"));
++    }
++}
++
++?>
++expect:
++    bool(false)
++    bool(false)
++    bool(false)
++<?php
++$test = new Selective();
++$test->start(PTHREADS_INHERIT_NONE);
++$test->join();
++?>
++=======================================
++expect:
++    bool(false)
++    bool(true)
++    bool(true)
++<?php
++$test = new Selective();
++$test->start(PTHREADS_INHERIT_ALL & ~PTHREADS_INHERIT_FUNCTIONS);
++$test->join();
++?>
++=======================================
++expect:
++    bool(false)
++    bool(false)
++    bool(true)
++<?php
++$test = new Selective();
++$test->start(PTHREADS_INHERIT_INI | PTHREADS_INHERIT_CONSTANTS);
++$test->join();
++?>
++=======================================
++expect:
++    bool(true)
++    bool(true)
++    bool(true)
++<?php
++$test = new Selective();
++$test->start();
++$test->join();
++?>
+diff --git a/ext/pthreads/examples/SharingResources.php b/ext/pthreads/examples/SharingResources.php
+new file mode 100644
+index 00000000..f0254e71
+--- /dev/null
++++ b/ext/pthreads/examples/SharingResources.php
+@@ -0,0 +1,68 @@
++<?php
++/*********************************************************************
++* Please note, this is NOT an example of stable code ...
++**********************************************************************
++*
++* MySQL is horrible, but it doesn't use an object and resource per connection as MySQLi does, 
++* because of this you are able to share a single mysql connection among contexts
++*
++********************************************************************************
++* THIS DOES NOT MAKE IT A GOOD IDEA, I REPEAT, THIS DOES NOT MAKE IT A GOOD IDEA
++********************************************************************************
++*
++* Some helpful notes ...
++*	If some logic crashes while you are sharing resources among contexts, try:
++		introduce synchronization
++		free results explicitly in the thread that queried for them
++		try another database driver
++		try another database server
++		disable any buffering that you can control ( client side )
++		if you're working on the command line try USE_ZEND_ALLOC=0, ie:
++			USE_ZEND_ALLOC=0 /usr/local/bin/php my-script.php
++	Please note that running without zend alloc is also officially unsupported
++		but can sometimes yield a predictable result, as in the case of this script.
++*
++*	The reasons logic may fail come down to reallocation of buffers in the underlying streams
++*	Zend's allocation mechanism is built to not share, when you try to reallocate a chunk of memory allocated in another thread
++*	zend will throw an error. Using the suggested environment variable may work around this, but leaves you open to memory errors
++*	that zend usually manages for extensions that rely on memory management freeing memory when necessary.
++*	Please remember that the resources as defined in C were never meant to be shared, they are designed with this in mind, and most
++*	types of resource WILL have problems.
++*
++* The preferable way to use MySQL is MySQLi or PDO, there is a connection-per-thread (much preferred) example using MySQLi in MySQLi.php
++*
++* This code remains to keep history in tact - ish
++*/
++class MyShared extends Thread {
++	public function __construct($mysql, $lock = null){
++		$this->mysql = $mysql;
++		$this->lock = $lock;
++	}
++
++	public function run(){
++		if ($this->lock) {
++			$this->lock->synchronized(function(){
++				$this->doStuff();
++			});
++		} else $this->doStuff();
++	}
++
++	private function doStuff() {
++		if (($result = mysql_query("SHOW PROCESSLIST;", $this->mysql))) {
++			while(($row = mysql_fetch_assoc($result))) {
++				print_r($row);
++			}
++		}
++	}
++}
++
++$mysql = mysql_connect("127.0.0.1", "root", "");
++if ($mysql) {
++	$lock = new Threaded();
++	$instances = array(new MyShared($mysql, $lock), new MyShared($mysql, $lock), new MyShared($mysql, $lock));
++	foreach($instances as $instance)
++		$instance->start();
++	foreach($instances as $instance)
++		$instance->join();
++}
++?>
+diff --git a/ext/pthreads/examples/SimpleWebRequest.php b/ext/pthreads/examples/SimpleWebRequest.php
+new file mode 100644
+index 00000000..eb818725
+--- /dev/null
++++ b/ext/pthreads/examples/SimpleWebRequest.php
+@@ -0,0 +1,39 @@
++<?php
++error_reporting(E_ALL);
++
++class WebRequest extends Thread {
++	public $url;
++	public $data;
++	
++	public function __construct($url){
++		$this->url = $url;
++	}
++	
++	public function run(){
++		if(($url = $this->url)){
++			/*
++			* If a large amount of data is being requested, you might want to
++			* fsockopen and read using usleep in between reads
++			*/
++			$this->data = file_get_contents($url);
++		} else printf("Thread #%lu was not provided a URL\n", $this->getThreadId());
++	}
++}
++
++$t = microtime(true);
++$g = new WebRequest(sprintf("http://www.google.com/?q=%s", rand()*10));
++/* starting synchronized */
++if($g->start()){
++	printf("Request took %f seconds to start ", microtime(true)-$t);
++	while($g->isRunning()){
++		echo ".";
++		$g->synchronized(function() use($g) {
++			$g->wait(100);
++		});
++	}
++	if ($g->join()){
++		printf(" and %f seconds to finish receiving %d bytes\n", microtime(true)-$t, strlen($g->data));
++	} else printf(" and %f seconds to finish, request failed\n", microtime(true)-$t);
++	
++}
++?>
+diff --git a/ext/pthreads/examples/Singleton.php b/ext/pthreads/examples/Singleton.php
+new file mode 100644
+index 00000000..88136945
+--- /dev/null
++++ b/ext/pthreads/examples/Singleton.php
+@@ -0,0 +1,58 @@
++<?php
++/*
++* Often an application will have singleton-like objects, such as PDO connections or such.
++* 
++* You cannot pass such objects into a Thread, because they do not gracefully or properly serialize
++* themselves and are not Threaded objects.
++*
++* Even if you could pass the object to a Thread, it would not be safe; They were never intended to be used that
++* way and don't have the machinery that Threaded objects have that make them safe to share.
++*
++* This may seem awkward, but the solution can be quite elegant, and is usually easy to achieve.
++*
++* This example shows the execution of some Collectables (the anon class) that require a connection to PDO.
++*/
++class PDOWorker extends Worker {
++
++	/*
++	* Note that we only pass in the configuration to create the connection
++	*/
++	public function __construct(array $config) {
++		$this->config = $config;
++	}
++
++	public function run() {
++		self::$connection = 
++			new PDO(...$this->config);
++	}
++
++	public function getConnection() { return self::$connection; }
++
++	private $config;
++	
++	/*
++	* static variables are treated as thread-local by pthreads
++	*/
++	private static $connection;
++}
++
++/*
++* When the Pool starts new Worker threads, they will construct the
++* PDO object before any Collectable objects are executed.
++*/
++$pool = new Pool(4, PDOWorker::class, [["sqlite:example.db"]]);
++
++/*
++* Now we can submit work to the Pool
++*/
++
++while (@$i++<10) {
++	$pool->submit(new class extends Threaded {
++		public function run() {
++			var_dump($this->worker->getConnection());
++		}
++	});
++}
++
++$pool->shutdown();
++?>
+diff --git a/ext/pthreads/examples/SocketServer.php b/ext/pthreads/examples/SocketServer.php
+new file mode 100644
+index 00000000..96cbefc2
+--- /dev/null
++++ b/ext/pthreads/examples/SocketServer.php
+@@ -0,0 +1,81 @@
++<?php
++/*
++* @NOTE
++*	RESOURCES ARE BEING TRIED OUT, THIS MAY CRASH OR KILL THE EXECUTOR OF THE SCRIPT: NOT MY FAULT
++*
++*	It's pretty clear that people want resources to work, and I cannot deny it would be useful.
++*	Officially, resources are unsupported, if it doesn't work there is NOTHING I can do about it.
++*	I do NOT have the time to make every kind of resource safe, or ensure compatibility.
++*	I can apply some cleverness to make sure resources are destroyed by whoever creates them, that is ALL.
++*	In the case of sockets, this appears to work, the code below ran all day on pthreads.org without interruption.
++*	AGAIN: 	If this does not work, there is NOTHING more I can do. 
++			If a particular type of resource wigs out, there is NOTHING I can do.
++			If this kills you, horribly, there is NOTHING I can do.
++*/
++class Client extends Thread {
++	public function __construct($socket){
++		$this->socket = $socket;
++		$this->start();
++	}
++	public function run(){
++		$client = $this->socket;
++		if ($client) {
++			$header = 0;
++			while(($chars = socket_read($client, 1024, PHP_NORMAL_READ))) {
++				$head[$header]=trim($chars);
++				if ($header>0) {
++					if (!$head[$header] && !$head[$header-1])
++						break;
++				}
++				$header++;
++			}
++			foreach($head as $header) {
++				if ($header) {
++					$headers[]=$header;	
++				}
++			}
++
++			$response = array(	
++				"head" => array(
++					"HTTP/1.0 200 OK",
++					"Content-Type: text/html"
++				), 
++				"body" => array()
++			);
++			
++			socket_getpeername($client, $address, $port);
++		
++			$response["body"][]="<html>";
++			$response["body"][]="<head>";
++			$response["body"][]="<title>Multithread Sockets PHP ({$address}:{$port})</title>";
++			$response["body"][]="</head>";
++			$response["body"][]="<body>";
++			$response["body"][]="<pre>";
++			foreach($headers as $header)
++				$response["body"][]="{$header}";
++			$response["body"][]="</pre>";
++			$response["body"][]="</body>";
++			$response["body"][]="</html>";
++			$response["body"] = implode("\r\n", $response["body"]);
++			$response["head"][] = sprintf("Content-Length: %d", strlen($response["body"]));
++			$response["head"] = implode("\r\n", $response["head"]);
++				
++			socket_write($client, $response["head"]);
++			socket_write($client, "\r\n\r\n");
++			socket_write($client, $response["body"]);
++					
++			socket_close($client);
++		}
++	}
++}
++/* ladies and gentlemen, the world first multi-threaded socket server in PHP :) */
++$server = socket_create_listen(13000);
++while(($client = socket_accept($server))){
++	$clients[]=new Client($client);
++	/* we will serve a few clients and quit, to show that memory is freed and there are no errors on shutdown (hopefully) */
++	/* in the real world, do something here to ensure clients not running are destroyed */
++	/* the nature of a socket server is an endless loop, 
++		if you do not do something to explicitly destroy clients you create this may leak */
++}
++/* that is all */
++?>
+diff --git a/ext/pthreads/examples/Sockets.php b/ext/pthreads/examples/Sockets.php
+new file mode 100644
+index 00000000..aeac85c6
+--- /dev/null
++++ b/ext/pthreads/examples/Sockets.php
+@@ -0,0 +1,34 @@
++<?php
++/*
++* Please don't use resources, it's dangerous ...
++*/
++class Test extends Thread {
++	public $stop = false;
++	
++	public function __construct($socket){
++		$this->socket = $socket;
++	}
++	
++	public function run(){
++		while(++$clients < 10 &&
++			($client = socket_accept($this->socket))){
++			printf("Accept in %lu\n", $this->getThreadId());
++			var_dump($client);
++			var_dump($this->socket);
++			socket_close($client);
++		}
++	}
++}
++$workers = array();
++$sock = socket_create_listen($argv[1]);
++if ($sock) {	
++	while(++$worker<5){
++		$workers[$worker] = new Test($sock);
++		$workers[$worker]->start();
++	}
++	printf("%d threads waiting on port %d\n", count($workers), $argv[1]);
++}
++
++foreach ($workers as $thread)
++    $thread->join();
++?>
+diff --git a/ext/pthreads/examples/StackableArray.php b/ext/pthreads/examples/StackableArray.php
+new file mode 100644
+index 00000000..780fa6c5
+--- /dev/null
++++ b/ext/pthreads/examples/StackableArray.php
+@@ -0,0 +1,65 @@
++<?php
++/**
++* NOTES:
++*	You can and should use a Threaded as a base object for all arrays you wish to pass to other threads
++*	Members of arrays that are themselves an array should also be a derivative of Threaded, though it's not strictly necessary.
++*	Annonymous members are supported ([] assignments)
++*	Even if 100(0) threads are accessing the array, nothing bad will happen
++*	Can anyone think of anything else ?
++*/
++
++/* you might want to set this to 100 before running if you're running on older ( dual core ) hardware */
++/* never, _ever_, _ever_, _ever_ create 1000 threads in a PHP application, if you think there's a need to create that many threads:
++	you are doing it wrong */
++/* the number is high to show that manipulating a stackable as an array in this way is completely safe and reliable */
++$hammers = 500;
++/** $hammers threads are about to edit this array */
++/*
++* NOTE
++*	pthreads overrides the dimension read/writers with our own handlers
++*	Our internal handlers are not setup to execute ArrayAccess interfaces
++*	If we did execute ArrayAccess methods, you would pay a high price:
++*		referencing an array in this way would keep switching in and out of the VM to call your handlers,
++*		because these arrays are meant to provide efficiency using the ArrayAccess interface is unsuitable.
++*/
++class StackableArray extends Threaded {
++
++	/*
++	* Always think about caching these types of objects, don't waste the run method or your workers
++	*/
++    public function run() {}
++}
++/* a thread for editing */
++class T extends Thread {
++	public function __construct($test){
++		$this->test = $test;
++		$this->start();
++	}
++	public function run(){
++		/*
++		* NOTE
++		*	If your editing of an array is this simple
++		*		then don't protect these instructions
++		*		you will incurr additional unecessary locking
++		*/
++		$this->test[]=rand(0, 10);
++	}
++}
++/* create the array here for passing */
++$s = new StackableArray();
++/* set a pointless value */
++$s[]="h";
++/* show it was set */
++print_r($s);
++$ts = array();
++/* hammer the crap out of the array */
++while(@$i++ < $hammers){
++	$ts[]=new T($s);
++}
++printf("GOT %d T's\n", count($ts));
++/* we want all threads to complete */
++foreach($ts as $t)
++	$t->join();
++/* show it was all set without corruption */
++print_r($s);
++?>
+diff --git a/ext/pthreads/examples/Synchronization.php b/ext/pthreads/examples/Synchronization.php
+new file mode 100644
+index 00000000..dd2fdfdc
+--- /dev/null
++++ b/ext/pthreads/examples/Synchronization.php
+@@ -0,0 +1,36 @@
++<?php
++/*
++	Rules:
++		1. only ever wait FOR something
++		2. only ever wait FOR something
++		3. only ever wait FOR something
++	
++	Read the code ...
++*/
++$thread = new class extends Thread {
++	
++	public function run() {
++		
++		$this->synchronized(function(){
++			$this->awake = true;
++			$this->notify();
++		});
++	}
++};
++
++$thread->start();
++$thread->synchronized(function() use($thread) {
++	while (!$thread->awake) {
++		/*
++			If there was no precondition above and the Thread
++			managed to send notification before we entered this synchronized block
++			we would wait forever!
++
++			We check the precondition in a loop because a Thread can be awoken
++			by signals other than the one you are waiting for.
++		*/
++		$thread->wait();
++	}
++});
++$thread->join();
++?>
+diff --git a/ext/pthreads/examples/fetching-data-from-a-thread.php b/ext/pthreads/examples/fetching-data-from-a-thread.php
+new file mode 100644
+index 00000000..9bc40bdb
+--- /dev/null
++++ b/ext/pthreads/examples/fetching-data-from-a-thread.php
+@@ -0,0 +1,38 @@
++<?php
++
++/*
++Threaded objects (which subsequently includes Volatile objects) are tied to the
++context in which they are created. They can be used to fetch data from a thread,
++but must be created in the outer most thread in which they are used.
++*/
++
++// create Threaded object in the main thread
++$store = new Threaded();
++
++$thread = new class($store) extends Thread {
++	public $store;
++
++	public function __construct(Threaded $store)
++	{
++		$this->store = $store;
++	}
++
++	public function run()
++	{
++		/*
++		The following array cast is necessary to prevent implicit coercion to a
++		Volatile object. Without it, accessing $store in the main thread after
++		this thread has been destroyed would lead to RuntimeException of:
++		"pthreads detected an attempt to connect to an object which has already
++		been destroyed in %s:%d"
++
++		See this StackOverflow post for additional information:
++		https://stackoverflow.com/a/44852650/4530326
++		*/
++		$this->store[] = (array)[1,2,3];
++	}
++};
++
++$thread->start() && $thread->join();
++
++print_r($store);
+diff --git a/ext/pthreads/examples/stub.php b/ext/pthreads/examples/stub.php
+new file mode 100644
+index 00000000..5cf00652
+--- /dev/null
++++ b/ext/pthreads/examples/stub.php
+@@ -0,0 +1,698 @@
++<?php
++/**
++ * pthreads extension stub file for code completion purposes
++ *
++ * @author Lisachenko Alexander <lisachenko.it@gmail.com>
++ * @version 3.0.0
++ */
++
++/**
++ * The default inheritance mask used when starting Threads and Workers
++ */
++define('PTHREADS_INHERIT_ALL', 0x111111);
++
++/**
++ * Nothing will be inherited by the new context
++ */
++define('PTHREADS_INHERIT_NONE', 0);
++
++/**
++ * Determines whether the ini entries are inherited by the new context
++ */
++define('PTHREADS_INHERIT_INI', 0x1);
++
++/**
++ * Determines whether the constants are inherited by the new context
++ */
++define('PTHREADS_INHERIT_CONSTANTS', 0x10);
++
++/**
++ * Determines whether the class table is inherited by the new context
++ */
++define('PTHREADS_INHERIT_CLASSES', 0x100);
++
++/**
++ * Determines whether the function table is inherited by the new context
++ */
++define('PTHREADS_INHERIT_FUNCTIONS', 0x100);
++
++/**
++ * Determines whether the included_files table is inherited by the new context
++ */
++define('PTHREADS_INHERIT_INCLUDES', 0x10000);
++
++/**
++ * Determines whether the comments are inherited by the new context
++ */
++define('PTHREADS_INHERIT_COMMENTS', 0x100000);
++
++/**
++ * Allow output headers from the threads
++ */
++define('PTHREADS_ALLOW_HEADERS', 0x1000000);
++
++/**
++ * Threaded class
++ *
++ * Threaded objects form the basis of pthreads ability to execute user code in parallel;
++ * they expose and include synchronization methods and various useful interfaces.
++ *
++ * Threaded objects, most importantly, provide implicit safety for the programmer;
++ * all operations on the object scope are safe.
++ *
++ * @link http://www.php.net/manual/en/class.threaded.php
++ * @since 2.0.0
++ */
++class Threaded implements Traversable, Countable, ArrayAccess, Collectable
++{
++    /**
++     * Increments the object's reference count
++     */
++    public function addRef() {}
++
++    /**
++     * Fetches a chunk of the objects properties table of the given size
++     *
++     * @param int $size The number of items to fetch
++     * @param bool $preserve Preserve the keys of members
++     *
++     * @link http://www.php.net/manual/en/threaded.chunk.php
++     * @return array An array of items from the objects member table
++     */
++    public function chunk($size, $preserve = false) {}
++
++    /**
++     * {@inheritdoc}
++     */
++    public function count() {}
++
++    /**
++     * Decrements the object's reference count
++     */
++    public function delRef() {}
++
++    /**
++     * Runtime extending of the Threaded class
++     *
++     * @param string $class The name of the class to extend Threaded
++     * @return bool A boolean indication of success
++     */
++    public static function extend($class) {}
++
++    /**
++     * Gets the object's reference count
++     *
++     * @return int The object's reference count
++     */
++    public function getRefCount() {}
++
++    /**
++     * A default method for marking an object as ready to be destroyed
++     *
++     * @return bool(true) The referenced object can be destroyed
++     */
++    public function isGarbage() : bool{}
++
++    /**
++     * Tell if the referenced object is executing
++     *
++     * @link http://www.php.net/manual/en/threaded.isrunning.php
++     * @return bool A boolean indication of state
++     */
++    public function isRunning() {}
++
++    /**
++     * Tell if the referenced object exited, suffered fatal errors, or threw uncaught exceptions during execution
++     *
++     * @link http://www.php.net/manual/en/threaded.isterminated.php
++     * @return bool A boolean indication of state
++     */
++    public function isTerminated() {}
++
++    /**
++     * Merges data into the current object
++     *
++     * @param mixed $from The data to merge
++     * @param bool $overwrite Overwrite existing keys flag
++     *
++     * @link http://www.php.net/manual/en/threaded.merge.php
++     * @return bool A boolean indication of success
++     */
++    public function merge($from, $overwrite = true) {}
++
++    /**
++     * Send notification to the referenced object
++     *
++     * @link http://www.php.net/manual/en/threaded.notify.php
++     * @return bool A boolean indication of success
++     */
++    public function notify() {}
++
++    /**
++     * Send notification to one context waiting on the Threaded
++     *
++     * @return bool A boolean indication of success
++     */
++    public function notifyOne() {}
++
++    /**
++     * {@inheritdoc}
++     */
++    public function offsetGet($offset) {}
++
++    /**
++     * {@inheritdoc}
++     */
++    public function offsetSet($offset, $value) {}
++
++    /**
++     * {@inheritdoc}
++     */
++    public function offsetExists($offset) {}
++
++    /**
++     * {@inheritdoc}
++     */
++    public function offsetUnset($offset) {}
++
++    /**
++     * Pops an item from the objects property table
++     *
++     * @link http://www.php.net/manual/en/threaded.pop.php
++     * @return mixed The last item from the objects properties table
++     */
++    public function pop() {}
++
++    /**
++     * The programmer should always implement the run method for objects that are intended for execution.
++     *
++     * @link http://www.php.net/manual/en/threaded.run.php
++     * @return void The methods return value, if used, will be ignored
++     */
++    public function run() {}
++
++    /**
++     * Shifts an item from the objects properties table
++     *
++     * @link http://www.php.net/manual/en/threaded.shift.php
++     * @return mixed The first item from the objects properties table
++     */
++    public function shift() {}
++
++    /**
++     * Executes the block while retaining the synchronization lock for the current context.
++     *
++     * @param \Closure $function The block of code to execute
++     * @param mixed $args... Variable length list of arguments to use as function arguments to the block
++     *
++     * @link http://www.php.net/manual/en/threaded.synchronized.php
++     * @return mixed The return value from the block
++     */
++    public function synchronized(\Closure $function, ...$args) {}
++
++    /**
++     * Waits for notification from the Stackable
++     *
++     * @param int $timeout An optional timeout in microseconds
++     *
++     * @link http://www.php.net/manual/en/threaded.wait.php
++     * @return bool A boolean indication of success
++     */
++    public function wait(int $timeout = 0) {}
++}
++
++/**
++ * Volatile class
++ *
++ * The Volatile class is new to pthreads v3. Its introduction is a consequence of the new immutability semantics of
++ * Threaded members of Threaded classes. The Volatile class enables for mutability of its Threaded members, and is also
++ * used to store PHP arrays in Threaded contexts.
++ *
++ * @link http://php.net/manual/en/class.volatile.php
++ * @since 3.0.0
++ */
++class Volatile extends Threaded{
++}
++
++/**
++ * Basic thread implementation
++ *
++ * An implementation of a Thread should extend this declaration, implementing the run method.
++ * When the start method of that object is called, the run method code will be executed in separate Thread.
++ *
++ * @link http://www.php.net/manual/en/class.thread.php
++ */
++class Thread extends Threaded
++{
++    /**
++     * Will return the identity of the Thread that created the referenced Thread
++     *
++     * @link http://www.php.net/manual/en/thread.getcreatorid.php
++     * @return int A numeric identity
++     */
++    public function getCreatorId() {}
++
++    /**
++     * Will return the instance of currently executing thread
++     *
++     * @return static
++     */
++    public static function getCurrentThread() {}
++
++    /**
++     * Will return the identity of the currently executing thread
++     *
++     * @link http://www.php.net/manual/en/thread.getcurrentthreadid.php
++     * @return int
++     */
++    public static function getCurrentThreadId() {}
++
++    /**
++     * Will return the identity of the referenced Thread
++     *
++     * @link http://www.php.net/manual/en/thread.getthreadid.php
++     * @return int
++     */
++    public function getThreadId() {}
++
++    /**
++     * Tell if the referenced Thread has been joined by another context
++     *
++     * @link http://www.php.net/manual/en/thread.isjoined.php
++     * @return bool A boolean indication of state
++     */
++    public function isJoined() {}
++
++    /**
++     * Tell if the referenced Thread has been started
++     *
++     * @link http://www.php.net/manual/en/thread.isstarted.php
++     * @return bool A boolean indication of state
++     */
++    public function isStarted() {}
++
++    /**
++     * Causes the calling context to wait for the referenced Thread to finish executing
++     *
++     * @link http://www.php.net/manual/en/thread.join.php
++     * @return bool A boolean indication of state
++     */
++    public function join() {}
++
++    /**
++     * Will start a new Thread to execute the implemented run method
++     *
++     * @param int $options An optional mask of inheritance constants, by default PTHREADS_INHERIT_ALL
++     *
++     * @link http://www.php.net/manual/en/thread.start.php
++     * @return bool A boolean indication of success
++     */
++    public function start(int $options = PTHREADS_INHERIT_ALL) {}
++}
++
++/**
++ * Worker
++ *
++ * Worker Threads have a persistent context, as such should be used over Threads in most cases.
++ *
++ * When a Worker is started, the run method will be executed, but the Thread will not leave until one
++ * of the following conditions are met:
++ *   - the Worker goes out of scope (no more references remain)
++ *   - the programmer calls shutdown
++ *   - the script dies
++ * This means the programmer can reuse the context throughout execution; placing objects on the stack of
++ * the Worker will cause the Worker to execute the stacked objects run method.
++ *
++ * @link http://www.php.net/manual/en/class.worker.php
++ */
++class Worker extends Thread
++{
++    /**
++     * Executes the optional collector on each of the tasks, removing the task if true is returned
++     *
++     * @param callable $function The collector to be executed upon each task
++     * @return int The number of tasks left to be collected
++     */
++    public function collect(callable $function = null) {}
++
++    /**
++     * Executes the collector on the collectable object passed
++     *
++     * @param Collectable $collectable The collectable object to run the collector on
++     * @return bool The referenced object can be destroyed
++     */
++    public function collector(Collectable $collectable) {}
++
++    /**
++     * Returns the number of threaded tasks waiting to be executed by the referenced Worker
++     *
++     * @link http://www.php.net/manual/en/worker.getstacked.php
++     * @return int An integral value
++     */
++    public function getStacked() {}
++
++    /**
++     * Tell if the referenced Worker has been shutdown
++     *
++     * @link http://www.php.net/manual/en/worker.isshutdown.php
++     * @return bool A boolean indication of state
++     */
++    public function isShutdown() {}
++
++    /**
++     * Shuts down the Worker after executing all the threaded tasks previously stacked
++     *
++     * @link http://www.php.net/manual/en/worker.shutdown.php
++     * @return bool A boolean indication of success
++     */
++    public function shutdown() {}
++
++    /**
++     * Appends the referenced object to the stack of the referenced Worker
++     *
++     * @param Threaded $work Threaded object to be executed by the referenced Worker
++     *
++     * @link http://www.php.net/manual/en/worker.stack.php
++     * @return int The new length of the stack
++     */
++    public function stack(Threaded $work) {}
++
++    /**
++     * Removes the first task (the oldest one) in the stack.
++     *
++     * @link http://www.php.net/manual/en/worker.unstack.php
++     * @return Collectable|null The item removed from the stack
++     */
++    public function unstack() {}
++}
++
++/**
++ * Pool class
++ *
++ * A Pool is a container for, and controller of, a number of Worker threads, the number of threads can be adjusted
++ * during execution, additionally the Pool provides an easy mechanism to maintain and collect references in the
++ * proper way.
++ *
++ * @link http://www.php.net/manual/en/class.pool.php
++ */
++class Pool
++{
++    /**
++     * The maximum number of Worker threads allowed in this Pool
++     *
++     * @var integer
++     */
++    protected $size;
++
++    /**
++     * The name of the Worker class for this Pool
++     *
++     * @var string
++     */
++    protected $class;
++
++    /**
++     * The array of Worker threads for this Pool
++     *
++     * @var array|Worker[]
++     */
++    protected $workers;
++
++    /**
++     * The constructor arguments to be passed by this Pool to new Workers upon construction
++     *
++     * @var array
++     */
++    protected $ctor;
++
++    /**
++     * The numeric identifier for the last Worker used by this Pool
++     *
++     * @var integer
++     */
++    protected $last;
++
++    /**
++     * Construct a new Pool of Workers
++     *
++     * @param integer $size The maximum number of Workers this Pool can create
++     * @param string $class The class for new Workers
++     * @param array $ctor An array of arguments to be passed to new Workers
++     *
++     * @link http://www.php.net/manual/en/pool.__construct.php
++     */
++    public function __construct(int $size, string $class = Worker::class, array $ctor = array()) {}
++
++    /**
++     * Collect references to completed tasks
++     *
++     * Allows the Pool to collect references determined to be garbage by the given collector
++     *
++     * @param callable $collector
++     *
++     * @link http://www.php.net/manual/en/pool.collect.php
++     */
++    public function collect(callable $collector = null) {}
++
++    /**
++     * Resize the Pool
++     *
++     * @param integer $size The maximum number of Workers this Pool can create
++     *
++     * @link http://www.php.net/manual/en/pool.resize.php
++     */
++    public function resize(int $size) {}
++
++    /**
++     * Shutdown all Workers in this Pool
++     *
++     * @link http://www.php.net/manual/en/pool.shutdown.php
++     */
++    public function shutdown() {}
++
++    /**
++     * Submit the task to the next Worker in the Pool
++     *
++     * @param Threaded $task The task for execution
++     *
++     * @return int the identifier of the Worker executing the object
++     */
++    public function submit(Threaded $task) {}
++
++    /**
++     * Submit the task to the specific Worker in the Pool
++     *
++     * @param int $worker The worker for execution
++     * @param Threaded $task The task for execution
++     *
++     * @return int the identifier of the Worker that accepted the object
++     */
++    public function submitTo(int $worker, Threaded $task) {}
++}
++
++/**
++ * Collectable Class
++ *
++ * Garbage Collection interface for references to objects on Worker stacks
++ *
++ * @link http://www.php.net/manual/en/class.collectable.php
++ */
++interface Collectable
++{
++    /**
++     * Determine whether an object is ready to be destroyed
++     *
++     * @return bool Whether the referenced object can be destroyed
++     */
++    public function isGarbage() : bool;
++}
++
++class Socket extends \Threaded
++{
++    public const AF_UNIX = 1;
++    public const AF_INET = 2;
++    public const AF_INET6 = 10;
++    public const SOCK_STREAM = 1;
++    public const SOCK_DGRAM = 2;
++    public const SOCK_RAW = 3;
++    public const SOCK_SEQPACKET = 5;
++    public const SOCK_RDM = 4;
++    public const SO_DEBUG = 1;
++    public const SO_REUSEADDR = 2;
++    public const SO_REUSEPORT = 15;
++    public const SO_KEEPALIVE = 9;
++    public const SO_DONTROUTE = 5;
++    public const SO_LINGER = 13;
++    public const SO_BROADCAST = 6;
++    public const SO_OOBINLINE = 10;
++    public const SO_SNDBUF = 7;
++    public const SO_RCVBUF = 8;
++    public const SO_SNDLOWAT = 19;
++    public const SO_RCVLOWAT = 18;
++    public const SO_SNDTIMEO = 21;
++    public const SO_RCVTIMEO = 20;
++    public const SO_TYPE = 3;
++    public const SO_ERROR = 4;
++    public const SO_BINDTODEVICE = 25;
++    public const SOMAXCONN = 128;
++    public const TCP_NODELAY = 1;
++    public const SOL_SOCKET = 1;
++    public const SOL_TCP = 6;
++    public const SOL_UDP = 17;
++    public const MSG_OOB = 1;
++    public const MSG_WAITALL = 256;
++    public const MSG_CTRUNC = 8;
++    public const MSG_TRUNC = 32;
++    public const MSG_PEEK = 2;
++    public const MSG_DONTROUTE = 4;
++    public const MSG_EOR = 128;
++    public const MSG_CONFIRM = 2048;
++    public const MSG_ERRQUEUE = 8192;
++    public const MSG_NOSIGNAL = 16384;
++    public const MSG_MORE = 32768;
++    public const MSG_WAITFORONE = 65536;
++    public const MSG_CMSG_CLOEXEC = 1073741824;
++    public const EPERM = 1;
++    public const ENOENT = 2;
++    public const EINTR = 4;
++    public const EIO = 5;
++    public const ENXIO = 6;
++    public const E2BIG = 7;
++    public const EBADF = 9;
++    public const EAGAIN = 11;
++    public const ENOMEM = 12;
++    public const EACCES = 13;
++    public const EFAULT = 14;
++    public const ENOTBLK = 15;
++    public const EBUSY = 16;
++    public const EEXIST = 17;
++    public const EXDEV = 18;
++    public const ENODEV = 19;
++    public const ENOTDIR = 20;
++    public const EISDIR = 21;
++    public const EINVAL = 22;
++    public const ENFILE = 23;
++    public const EMFILE = 24;
++    public const ENOTTY = 25;
++    public const ENOSPC = 28;
++    public const ESPIPE = 29;
++    public const EROFS = 30;
++    public const EMLINK = 31;
++    public const EPIPE = 32;
++    public const ENAMETOOLONG = 36;
++    public const ENOLCK = 37;
++    public const ENOSYS = 38;
++    public const ENOTEMPTY = 39;
++    public const ELOOP = 40;
++    public const EWOULDBLOCK = 11;
++    public const ENOMSG = 42;
++    public const EIDRM = 43;
++    public const ECHRNG = 44;
++    public const EL2NSYNC = 45;
++    public const EL3HLT = 46;
++    public const EL3RST = 47;
++    public const ELNRNG = 48;
++    public const EUNATCH = 49;
++    public const ENOCSI = 50;
++    public const EL2HLT = 51;
++    public const EBADE = 52;
++    public const EBADR = 53;
++    public const EXFULL = 54;
++    public const ENOANO = 55;
++    public const EBADRQC = 56;
++    public const EBADSLT = 57;
++    public const ENOSTR = 60;
++    public const ENODATA = 61;
++    public const ETIME = 62;
++    public const ENOSR = 63;
++    public const ENONET = 64;
++    public const EREMOTE = 66;
++    public const ENOLINK = 67;
++    public const EADV = 68;
++    public const ESRMNT = 69;
++    public const ECOMM = 70;
++    public const EPROTO = 71;
++    public const EMULTIHOP = 72;
++    public const EBADMSG = 74;
++    public const ENOTUNIQ = 76;
++    public const EBADFD = 77;
++    public const EREMCHG = 78;
++    public const ERESTART = 85;
++    public const ESTRPIPE = 86;
++    public const EUSERS = 87;
++    public const ENOTSOCK = 88;
++    public const EDESTADDRREQ = 89;
++    public const EMSGSIZE = 90;
++    public const EPROTOTYPE = 91;
++    public const ENOPROTOOPT = 92;
++    public const EPROTONOSUPPORT = 93;
++    public const ESOCKTNOSUPPORT = 94;
++    public const EOPNOTSUPP = 95;
++    public const EPFNOSUPPORT = 96;
++    public const EAFNOSUPPORT = 97;
++    public const EADDRINUSE = 98;
++    public const EADDRNOTAVAIL = 99;
++    public const ENETDOWN = 100;
++    public const ENETUNREACH = 101;
++    public const ENETRESET = 102;
++    public const ECONNABORTED = 103;
++    public const ECONNRESET = 104;
++    public const ENOBUFS = 105;
++    public const EISCONN = 106;
++    public const ENOTCONN = 107;
++    public const ESHUTDOWN = 108;
++    public const ETOOMANYREFS = 109;
++    public const ETIMEDOUT = 110;
++    public const ECONNREFUSED = 111;
++    public const EHOSTDOWN = 112;
++    public const EHOSTUNREACH = 113;
++    public const EALREADY = 114;
++    public const EINPROGRESS = 115;
++    public const EISNAM = 120;
++    public const EREMOTEIO = 121;
++    public const EDQUOT = 122;
++    public const ENOMEDIUM = 123;
++    public const EMEDIUMTYPE = 124;
++    public const NORMAL_READ = 1;
++    public const BINARY_READ = 2;
++
++    public function __construct(int $domain, int $type, int $protocol){}
++
++    public function setOption(int $level, int $name, int $value) : bool{}
++
++    public function getOption(int $level, int $name) : int{}
++
++    public function bind(string $host, int $port = 0) : bool{}
++
++    public function listen(int $backlog = 0) : bool{}
++
++    public function accept($class = self::class){}
++
++    public function connect(string $host, int $port = 0) : bool{}
++
++    public static function select(array &$read, array &$write, array &$except, ?int $sec, int $usec = 0, int &$error = null){}
++
++    public function read(int $length, int $flags = 0, int $type = self::BINARY_READ){}
++
++    public function write(string $buffer, int $length = 0){}
++
++    public function send(string $buffer, int $length, int $flags){}
++
++    public function recvfrom(string &$buffer, int $length, int $flags, string &$name, int &$port = null){}
++
++    public function sendto(string $buffer, int $length, int $flags, string $addr, int $port = 0){}
++
++    public function setBlocking(bool $blocking) : bool{}
++
++    public function getPeerName(bool $port = true) : array{}
++
++    public function getSockName(bool $port = true) : array{}
++
++    public function close(){}
++
++    public function getLastError(bool $clear = false){}
++
++    public function clearError(){}
++
++    public static function strerror(int $error) : ?string{}
++}
+diff --git a/ext/pthreads/package.xml b/ext/pthreads/package.xml
+new file mode 100644
+index 00000000..3f540e75
+--- /dev/null
++++ b/ext/pthreads/package.xml
+@@ -0,0 +1,286 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<package packagerversion="1.9.4" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
++ <name>pthreads</name>
++ <channel>pecl.php.net</channel>
++ <summary>Threading API</summary>
++ <description>A compatible Threading API for PHP</description>
++ <lead>
++  <name>Joe Watkins</name>
++  <user>krakjoe</user>
++  <email>krakjoe@php.net</email>
++  <active>no</active>
++ </lead>
++ <developer>
++ 	<name>Bastian Schneider</name>
++ 	<user>sirsnyder</user>
++ 	<email>bastian.schneider@badnoob.com</email>
++ 	<active>yes</active>
++ </developer>
++ <date>2018-12-09</date>
++ <time>01:12:36</time>
++ <version>
++  <release>3.2.0</release>
++  <api>3.2.0</api>
++ </version>
++ <stability>
++  <release>stable</release>
++  <api>stable</api>
++ </stability>
++ <license uri="http://www.php.net/license">PHP License</license>
++ <notes>
++ php 7.2 support
++ anonymous class support
++ sockets are still experimental
++ fix #596
++ fix #585
++ fix #570, improve static property behaviour  
++ fix most warnings reported by #600 
++ fix #622
++ fix #616 
++ fix #657
++ fix #665
++ fix #564
++ fix #580
++ fix #681 
++ fix #666
++ fix #505
++ fix #658
++ fix #659
++ fix #729
++ fix #725
++ fix #733
++ fix #782
++ fix #797
++ fix #788
++ fix #780
++ fix #801
++ fix #816
++ fix #819
++ fix #836
++ fix #706
++ fix #874
++ fix #860
++ fix #890
++ fix #888
++ and many more improvements
++ 
++ Thanks to @dktapps and @tpunt for your support!
++ </notes>
++ <contents>
++  <dir name="/">
++   	<file name="examples/Benchmark.php" role="doc" />
++	<file name="examples/CallAnyFunction.php" role="doc" />
++	<file name="examples/ChannelsAndMagic.php" role="doc" />
++	<file name="examples/ClosureFuture.php" role="doc" />
++	<file name="examples/fetching-data-from-a-thread.php" role="doc" />
++	<file name="examples/KeepAliveSession.php" role="doc" />
++	<file name="examples/MySQLi.php" role="doc" />
++	<file name="examples/NonBlockingSocketServer.php" role="doc" />
++	<file name="examples/ObjectsAsParameters.php" role="doc" />
++	<file name="examples/Pooling.php" role="doc" />
++	<file name="examples/SelectiveInheritance.php" role="doc" />
++	<file name="examples/SharingResources.php" role="doc" />
++	<file name="examples/SimpleWebRequest.php" role="doc" />
++	<file name="examples/Singleton.php" role="doc" />
++	<file name="examples/Sockets.php" role="doc" />
++	<file name="examples/SocketServer.php" role="doc" />
++	<file name="examples/StackableArray.php" role="doc" />
++	<file name="examples/stub.php" role="doc" />
++	<file name="examples/Synchronization.php" role="doc" />
++   	<file name="tests/assets/ExternalClosureDefinition.php" role="test" />
++   	<file name="tests/assets/ExternalConstantsInterface.php" role="test" />
++   	<file name="tests/anon-bound-inherited.phpt" role="test" />
++   	<file name="tests/anon-interceptors.phpt" role="test" />
++   	<file name="tests/anon-unbound-double-inherited.phpt" role="test" />
++	<file name="tests/anon-unbound-inherited.phpt" role="test" />
++   	<file name="tests/anon-unbound-use-guards.phpt" role="test" />
++	<file name="tests/binding.phpt" role="test" />
++	<file name="tests/callstatic-etc.phpt" role="test" />
++   	<file name="tests/child-to-parent-class-copying-helper.php" role="test" />
++   	<file name="tests/child-to-parent-class-copying.phpt" role="test" />
++	<file name="tests/chunk.phpt" role="test" />
++	<file name="tests/class-defaults.phpt" role="test" />
++	<file name="tests/class-static-properties-2.phpt" role="test" />
++	<file name="tests/class-static-properties-double-inheritance.phpt" role="test" />
++	<file name="tests/class-static-properties-order-dependencies-inheritance.phpt" role="test" />
++	<file name="tests/class-static-properties-order-dependencies.phpt" role="test" />
++	<file name="tests/class-static-properties.phpt" role="test" />
++	<file name="tests/closure-delayed-include.phpt" role="test" />
++	<file name="tests/closure-secondary-thread-copy.phpt" role="test" />
++	<file name="tests/closures-as-members-repeated-reallocation.phpt" role="test" />
++	<file name="tests/closures-as-members.phpt" role="test" />
++	<file name="tests/complex-statics-set-null.phpt" role="test" />
++	<file name="tests/complex-statics.phpt" role="test" />
++	<file name="tests/constant-array.phpt" role="test" />
++	<file name="tests/constant-ast-copy-missing-type.phpt" role="test" />
++	<file name="tests/constant-ast-copy.phpt" role="test" />
++	<file name="tests/constants.phpt" role="test" />
++	<file name="tests/corrupted-resources.phpt" role="test" />
++	<file name="tests/counting-members.phpt" role="test" />
++	<file name="tests/crazy-refcounting-stuff.phpt" role="test" />
++	<file name="tests/display-startup-errors-output.phpt" role="test" />
++	<file name="tests/doc-comments.phpt" role="test" />
++	<file name="tests/exception-handler-caught-nested.phpt" role="test" />
++	<file name="tests/exception-handler-caught-uncaught.phpt" role="test" />
++	<file name="tests/exception-handler-fail.phpt" role="test" />
++	<file name="tests/exception-handler-options.phpt" role="test" />
++	<file name="tests/exception-handler.phpt" role="test" />
++	<file name="tests/explicit-cast-arrays.phpt" role="test" />
++	<file name="tests/foreign-private-members.phpt" role="test" />
++	<file name="tests/functions.phpt" role="test" />
++	<file name="tests/global-constants.phpt" role="test" />
++	<file name="tests/gone.phpt" role="test" />
++	<file name="tests/graceful-fatalities.phpt" role="test" />
++	<file name="tests/hacking-serials.phpt" role="test" />
++	<file name="tests/immutable-threaded-members.phpt" role="test" />
++	<file name="tests/included-files.phpt" role="test" />
++	<file name="tests/include_interface_constant.phpt" role="test" />
++	<file name="tests/includeme.inc" role="test" />
++	<file name="tests/inherit-non-closures.phpt" role="test" />
++	<file name="tests/inherited-anon-class-outside-context.phpt" role="test" />
++	<file name="tests/inherited-anon-class.phpt" role="test" />
++	<file name="tests/ini.phpt" role="test" />
++	<file name="tests/interface.phpt" role="test" />
++	<file name="tests/interfaces.phpt" role="test" />
++	<file name="tests/ipv6_skipif.inc" role="test" />
++	<file name="tests/isset-empty-compat.phpt" role="test" />
++	<file name="tests/isset-unset.phpt" role="test" />
++	<file name="tests/iterator-funcs.phpt" role="test" />
++	<file name="tests/iterator-no-refs.phpt" role="test" />
++	<file name="tests/legacy-ctor.phpt" role="test" />
++	<file name="tests/lexical-vars.phpt" role="test" />
++	<file name="tests/magic-methods-get-set.phpt" role="test" />
++	<file name="tests/magic-methods-isset-unset.phpt" role="test" />
++	<file name="tests/merge-non-threaded.phpt" role="test" />
++	<file name="tests/merging-members.phpt" role="test" />
++	<file name="tests/merging-range.phpt" role="test" />
++	<file name="tests/nested-non-threaded-object-references.phpt" role="test" />
++	<file name="tests/new-class-after-thread-creation.phpt" role="test" />
++	<file name="tests/no-cloning.phpt" role="test" />
++	<file name="tests/no-inheritance-anon-class.phpt" role="test" />
++	<file name="tests/norefs.phpt" role="test" />
++	<file name="tests/normal-reads.phpt" role="test" />
++	<file name="tests/normalize-members.phpt" role="test" />
++	<file name="tests/null-member-crash.phpt" role="test" />
++	<file name="tests/object-cast.phpt" role="test" />
++	<file name="tests/object-comparison.phpt" role="test" />
++	<file name="tests/oomethods.phpt" role="test" />
++	<file name="tests/pools-default.phpt" role="test" />
++	<file name="tests/pools.phpt" role="test" />
++	<file name="tests/private-class-constant-fetching.phpt" role="test" />
++	<file name="tests/pthreads-inherit-none-class-constant.phpt" role="test" />
++	<file name="tests/pthreads-inherit-none-class-constant-inherited.phpt" role="test" />
++	<file name="tests/recv-overload.phpt" role="test" />
++	<file name="tests/resource-keeping.phpt" role="test" />
++	<file name="tests/return-types.phpt" role="test" />
++	<file name="tests/run-time-cache-corruption.phpt" role="test" />
++	<file name="tests/runtime-inheritance-volatile.phpt" role="test" />
++	<file name="tests/runtime-inheritance.phpt" role="test" />
++	<file name="tests/selective-inheritance.phpt" role="test" />
++	<file name="tests/shift-pop.phpt" role="test" />
++	<file name="tests/shutdown-handler.phpt" role="test" />
++	<file name="tests/socket-bind.phpt" role="test" />
++	<file name="tests/socket-connect-error-unix.phpt" role="test" />
++	<file name="tests/socket-connect-error-win32.phpt" role="test" />
++	<file name="tests/socket-connect-params-af-inet.phpt" role="test" />
++	<file name="tests/socket-connect-params-af-unix.phpt" role="test" />
++	<file name="tests/socket-construct.phpt" role="test" />
++	<file name="tests/socket-getoption.phpt" role="test" />
++	<file name="tests/socket-getpeername-af-inet.phpt" role="test" />
++	<file name="tests/socket-getpeername-af-unix.phpt" role="test" />
++	<file name="tests/socket-getsockname-af-inet.phpt" role="test" />
++	<file name="tests/socket-getsockname-af-unix.phpt" role="test" />
++	<file name="tests/socket-ipv4loop.phpt" role="test" />
++	<file name="tests/socket-ipv6loop.phpt" role="test" />
++	<file name="tests/socket-read-type.phpt" role="test" />
++	<file name="tests/socket-select-unix.phpt" role="test" />
++	<file name="tests/socket-select-win32.phpt" role="test" />
++	<file name="tests/socket-sentto-recvfrom-ipv4-udp.phpt" role="test" />
++	<file name="tests/socket-sentto-recvfrom-ipv6-udp.phpt" role="test" />
++	<file name="tests/socket-sentto-recvfrom-unix.phpt" role="test" />
++	<file name="tests/socket-setoption.phpt" role="test" />
++	<file name="tests/socket-unixloop.phpt" role="test" />
++	<file name="tests/stateful-fatalities.phpt" role="test" />
++	<file name="tests/static-inheritance-unexpected-null.phpt" role="test" />
++	<file name="tests/static-resolution.phpt" role="test" />
++	<file name="tests/static-scope-vars.phpt" role="test" />
++	<file name="tests/staticmethods.phpt" role="test" />
++	<file name="tests/statics-thorn-in-other-side.phpt" role="test" />
++	<file name="tests/statics.phpt" role="test" />
++	<file name="tests/synchronization.phpt" role="test" />
++	<file name="tests/thorn-in-side.phpt" role="test" />
++	<file name="tests/thread.phpt" role="test" />
++	<file name="tests/threaded_valid_tsrmls.phpt" role="test" />
++	<file name="tests/threaded-extend-abstract.phpt" role="test" />
++	<file name="tests/trait-alias-bug.phpt" role="test" />
++	<file name="tests/trait-aliases-reflection.phpt" role="test" />
++	<file name="tests/trait-aliases.phpt" role="test" />
++	<file name="tests/traits-scope.inc" role="test" />
++	<file name="tests/traits-scope.phpt" role="test" />
++	<file name="tests/unprepared-entry-static-segfault.phpt" role="test" />
++	<file name="tests/unset-defaults.phpt" role="test" />
++	<file name="tests/unstack-running.phpt" role="test" />
++	<file name="tests/verify-overload.phpt" role="test" />
++	<file name="tests/volatile-arrays.phpt" role="test" />
++	<file name="tests/volatile-nested.phpt" role="test" />
++	<file name="tests/volatile-objects.phpt" role="test" />
++	<file name="tests/wait.phpt" role="test" />
++	<file name="tests/waiting-timeouts.phpt" role="test" />
++	<file name="tests/workers-constant-copy.phpt" role="test" />
++	<file name="tests/workers-default-collector.phpt" role="test" />
++	<file name="tests/workers-no-collect.phpt" role="test" />
++	<file name="tests/workers-no-join.phpt" role="test" />
++	<file name="tests/workers-no-shutdown-before-start.phpt" role="test" />
++	<file name="tests/workers-no-stack.phpt" role="test" />
++	<file name="tests/workers-no-start.phpt" role="test" />
++	<file name="tests/workers-no-unstack.phpt" role="test" />
++	<file name="tests/workers-ok-unstack.phpt" role="test" />
++   	<file name="src/sockets/windows_common.h" role="src" />
++   	<file name="src/copy.h" role="src" />
++	<file name="src/globals.c" role="src" />
++	<file name="src/globals.h" role="src" />
++	<file name="src/handlers.c" role="src" />
++	<file name="src/handlers.h" role="src" />
++	<file name="src/monitor.c" role="src" />
++	<file name="src/monitor.h" role="src" />
++	<file name="src/object.c" role="src" />
++	<file name="src/object.h" role="src" />
++	<file name="src/prepare.c" role="src" />
++	<file name="src/prepare.h" role="src" />
++	<file name="src/pthreads.h" role="src" />
++	<file name="src/resources.c" role="src" />
++	<file name="src/resources.h" role="src" />
++	<file name="src/socket.c" role="src" />
++	<file name="src/socket.h" role="src" />
++	<file name="src/stack.c" role="src" />
++	<file name="src/stack.h" role="src" />
++	<file name="src/store.c" role="src" />
++	<file name="src/store.h" role="src" />
++	<file name="src/thread.h" role="src" />
++   	<file name="classes/collectable.h" role="src" />
++	<file name="classes/pool.h" role="src" />
++	<file name="classes/socket.h" role="src" />
++	<file name="classes/thread.h" role="src" />
++	<file name="classes/threaded.h" role="src" />
++	<file name="classes/worker.h" role="src" />
++	<file name="config.m4" role="src" />
++	<file name="config.w32" role="src" />
++	<file name="LICENSE" role="doc" />
++	<file name="php_pthreads.c" role="src" />
++	<file name="php_pthreads.h" role="src" />
++	<file name="README.md" role="doc" />
++  </dir>
++ </contents>
++ <dependencies>
++  <required>
++   <php>
++    <min>7.2.0</min>
++   </php>
++   <pearinstaller>
++    <min>1.4.0b1</min>
++   </pearinstaller>
++  </required>
++ </dependencies>
++ <providesextension>pthreads</providesextension>
++ <extsrcrelease />
++</package>
+diff --git a/ext/pthreads/php_pthreads.c b/ext/pthreads/php_pthreads.c
+new file mode 100644
+index 00000000..2d93fcbc
+--- /dev/null
++++ b/ext/pthreads/php_pthreads.c
+@@ -0,0 +1,898 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2014                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <joe.watkins@live.co.uk>                         |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PHP_PTHREADS
++#define HAVE_PHP_PTHREADS
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++#ifndef HAVE_PHP_PTHREADS_H
++#	include <php_pthreads.h>
++#endif
++
++#ifndef HAVE_PTHREADS_OBJECT_H
++#	include <src/object.h>
++#endif
++
++#ifndef ZTS
++#	error "pthreads requires that Thread Safety is enabled, add --enable-maintainer-zts to your PHP build configuration"
++#endif
++
++#if PHP_VERSION_ID < 70200
++#	error "pthreads requires PHP 7.2, ZTS in versions 7.0 and 7.1 is broken"
++#endif
++
++#if COMPILE_DL_PTHREADS
++	ZEND_TSRMLS_CACHE_DEFINE();
++	ZEND_GET_MODULE(pthreads)
++#endif
++
++#ifndef HAVE_PTHREADS_GLOBALS_H
++#	include <src/globals.h>
++#endif
++
++#ifndef HAVE_PTHREADS_COPY_H
++#	include <src/copy.h>
++#endif
++
++zend_module_entry pthreads_module_entry = {
++  STANDARD_MODULE_HEADER,
++  PHP_PTHREADS_EXTNAME,
++  NULL,
++  PHP_MINIT(pthreads),
++  PHP_MSHUTDOWN(pthreads),
++  PHP_RINIT(pthreads),
++  PHP_RSHUTDOWN(pthreads),
++  PHP_MINFO(pthreads),
++  PHP_PTHREADS_VERSION,
++  NO_MODULE_GLOBALS,
++  ZEND_MODULE_POST_ZEND_DEACTIVATE_N(pthreads),
++  STANDARD_MODULE_PROPERTIES_EX
++};
++
++zend_class_entry *pthreads_threaded_entry;
++zend_class_entry *pthreads_volatile_entry;
++zend_class_entry *pthreads_thread_entry;
++zend_class_entry *pthreads_worker_entry;
++zend_class_entry *pthreads_collectable_entry;
++zend_class_entry *pthreads_pool_entry;
++zend_class_entry *pthreads_socket_entry;
++
++zend_object_handlers pthreads_handlers;
++zend_object_handlers pthreads_socket_handlers;
++zend_object_handlers *zend_handlers;
++void ***pthreads_instance = NULL;
++
++#ifndef HAVE_PTHREADS_OBJECT_H
++#	include <src/object.h>
++#endif
++
++ZEND_DECLARE_MODULE_GLOBALS(pthreads)
++
++typedef struct _pthreads_supported_sapi_t {
++	const char *name;
++	size_t      nlen;
++} pthreads_supported_sapi_t;
++
++const static pthreads_supported_sapi_t whitelist[] = {
++	{ZEND_STRL("cli")},
++	{ZEND_STRL("phpdbg")}, /* not really supported, needs work */
++	{ZEND_STRL("homegear")},
++	{NULL, 0}
++};
++
++static inline zend_bool pthreads_is_supported_sapi(char *name) {
++	const pthreads_supported_sapi_t *sapi = whitelist;
++	zend_long nlen = strlen(name);
++
++	while (sapi->name) {
++		if (nlen == sapi->nlen && 
++			memcmp(sapi->name, name, nlen) == SUCCESS) {
++			return 1;
++		}
++		sapi++;
++	}
++
++	return 0;
++}
++
++typedef void (*zend_execute_ex_function)(zend_execute_data *);
++
++zend_execute_ex_function zend_execute_ex_hook = NULL;
++
++static inline void pthreads_globals_ctor(zend_pthreads_globals *pg) {
++	ZVAL_UNDEF(&pg->this);
++	pg->pid = 0L;
++	pg->signal = 0;
++	pg->resources = NULL;
++}
++
++/* {{{ */
++static inline void pthreads_execute_ex(zend_execute_data *data) {
++	if (zend_execute_ex_hook) {
++		zend_execute_ex_hook(data);
++	} else execute_ex(data);
++	
++	if (Z_TYPE(PTHREADS_ZG(this)) != IS_UNDEF) {
++		if (EG(exception) && 
++			(!EG(current_execute_data) || !EG(current_execute_data)->prev_execute_data))
++			zend_try_exception_handler();
++	}
++} /* }}} */ 
++
++/* {{{ */
++static inline zend_bool pthreads_verify_type(zend_execute_data *execute_data, zval *var, zend_arg_info *info) {
++	if (!ZEND_TYPE_IS_SET(info->type)) {
++		return 1;
++	}
++
++	if (ZEND_TYPE_IS_CLASS(info->type)) {
++		pthreads_object_t *threaded;
++
++		if (!var || 
++			Z_TYPE_P(var) != IS_OBJECT || 
++			!instanceof_function(Z_OBJCE_P(var), pthreads_threaded_entry)) {
++			return 0;
++		}
++
++		threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(var));
++
++		if (!PTHREADS_IN_CREATOR(threaded)) {
++			zend_class_entry *ce;
++			void **cache = CACHE_ADDR(EX(opline)->op2.num);
++
++			if (*cache) {
++				ce = *cache;
++			} else {
++				ce = zend_fetch_class(ZEND_TYPE_NAME(info->type), 
++					(ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
++			
++				if (!ce) {
++					return Z_TYPE_P(var) == IS_NULL && ZEND_TYPE_ALLOW_NULL(info->type);
++				}
++
++				*cache = (void*) ce;
++			}
++
++			if (Z_TYPE_P(var) == IS_OBJECT) {
++				zend_class_entry *instance = zend_fetch_class(
++					threaded->std.ce->name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
++
++				if (!instance) {
++					return 0;
++				}
++
++				return instanceof_function(instance, ce);
++			}
++		}
++	}
++
++	return 0;
++} /* }}} */
++
++/* {{{ */
++static inline int php_pthreads_recv(zend_execute_data *execute_data) {
++	if (Z_TYPE(PTHREADS_ZG(this)) != IS_UNDEF) {
++		uint32_t arg_num = EX(opline)->op1.num;	
++		zval *var = NULL;
++
++		if (UNEXPECTED(arg_num > EX_NUM_ARGS())) {
++			return ZEND_USER_OPCODE_DISPATCH;	
++		}
++
++#if ZEND_USE_ABS_CONST_ADDR
++		if (EX(opline)->result_type == IS_CONST) {
++				var = (zval*) EX(opline)->result.var;	
++		} else var = EX_VAR(EX(opline)->result.num);
++#else
++		var = EX_VAR(EX(opline)->result.num);
++#endif
++
++		if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0)) {
++			if (pthreads_verify_type(execute_data, 
++				var, 
++				&EX(func)->common.arg_info[arg_num-1])) {
++				EX(opline)++;
++				return ZEND_USER_OPCODE_CONTINUE;
++			}
++		}
++	}
++	return ZEND_USER_OPCODE_DISPATCH;
++} /* }}} */
++
++/* {{{ */
++static inline int php_pthreads_verify_return_type(zend_execute_data *execute_data) {
++	if (Z_TYPE(PTHREADS_ZG(this)) != IS_UNDEF) {
++		zval *var = NULL;
++		
++		if (EX(opline)->op1_type == IS_UNUSED) {	
++			return ZEND_USER_OPCODE_DISPATCH;
++		}
++
++#if ZEND_USE_ABS_CONST_ADDR
++		if (EX(opline)->op1_type & IS_CONST) {
++			var = (zval*) EX(opline)->op1.var;
++		} else var = EX_VAR(EX(opline)->op1.num);
++#else
++		var = EX_VAR(EX(opline)->op1.num);
++#endif
++
++		if (pthreads_verify_type(execute_data, 
++			var,
++			EX(func)->common.arg_info - 1)) {
++			EX(opline)++;
++			return ZEND_USER_OPCODE_CONTINUE;
++		}
++	}
++
++	return ZEND_USER_OPCODE_DISPATCH;
++} /* }}} */
++
++PHP_MINIT_FUNCTION(pthreads)
++{
++	zend_class_entry ce;
++
++	if (!pthreads_is_supported_sapi(sapi_module.name)) {
++		zend_error(E_ERROR, "The %s SAPI is not supported by pthreads",
++			sapi_module.name);
++		return FAILURE;
++	}
++
++	zend_execute_ex_hook = zend_execute_ex;
++	zend_execute_ex = pthreads_execute_ex;	
++
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_ALL", PTHREADS_INHERIT_ALL, CONST_CS | CONST_PERSISTENT);
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_NONE", PTHREADS_INHERIT_NONE, CONST_CS | CONST_PERSISTENT);
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_INI", PTHREADS_INHERIT_INI, CONST_CS | CONST_PERSISTENT);
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_CONSTANTS", PTHREADS_INHERIT_CONSTANTS, CONST_CS | CONST_PERSISTENT);
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_CLASSES", PTHREADS_INHERIT_CLASSES, CONST_CS | CONST_PERSISTENT);
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_FUNCTIONS", PTHREADS_INHERIT_FUNCTIONS, CONST_CS | CONST_PERSISTENT);
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_INCLUDES", PTHREADS_INHERIT_INCLUDES, CONST_CS | CONST_PERSISTENT);
++	REGISTER_LONG_CONSTANT("PTHREADS_INHERIT_COMMENTS", PTHREADS_INHERIT_COMMENTS, CONST_CS | CONST_PERSISTENT);
++
++	REGISTER_LONG_CONSTANT("PTHREADS_ALLOW_HEADERS", PTHREADS_ALLOW_HEADERS, CONST_CS | CONST_PERSISTENT);
++
++	INIT_CLASS_ENTRY(ce, "Collectable", pthreads_collectable_methods);
++	pthreads_collectable_entry = zend_register_internal_interface(&ce);
++
++	INIT_CLASS_ENTRY(ce, "Threaded", pthreads_threaded_methods);
++	pthreads_threaded_entry=zend_register_internal_class(&ce);
++	pthreads_threaded_entry->get_iterator = pthreads_object_iterator_create;
++	pthreads_threaded_entry->create_object = pthreads_threaded_ctor;
++	pthreads_threaded_entry->serialize = pthreads_threaded_serialize;
++	pthreads_threaded_entry->unserialize = pthreads_threaded_unserialize;
++	zend_class_implements(pthreads_threaded_entry, 2, zend_ce_traversable, pthreads_collectable_entry);
++	
++	INIT_CLASS_ENTRY(ce, "Volatile", NULL);
++	pthreads_volatile_entry = zend_register_internal_class_ex(&ce, pthreads_threaded_entry);
++	
++	INIT_CLASS_ENTRY(ce, "Thread", pthreads_thread_methods);
++	pthreads_thread_entry=zend_register_internal_class_ex(&ce, pthreads_threaded_entry);
++	pthreads_thread_entry->create_object = pthreads_thread_ctor;
++
++	INIT_CLASS_ENTRY(ce, "Worker", pthreads_worker_methods);
++	pthreads_worker_entry=zend_register_internal_class_ex(&ce, pthreads_thread_entry);
++	pthreads_worker_entry->create_object = pthreads_worker_ctor;	
++
++	INIT_CLASS_ENTRY(ce, "Pool", pthreads_pool_methods);
++	pthreads_pool_entry=zend_register_internal_class(&ce);
++	zend_declare_property_long(pthreads_pool_entry, ZEND_STRL("size"), 1, ZEND_ACC_PROTECTED);
++	zend_declare_property_null(pthreads_pool_entry, ZEND_STRL("class"),   ZEND_ACC_PROTECTED);
++	zend_declare_property_null(pthreads_pool_entry, ZEND_STRL("workers"), ZEND_ACC_PROTECTED);
++	zend_declare_property_null(pthreads_pool_entry, ZEND_STRL("ctor"),    ZEND_ACC_PROTECTED);
++	zend_declare_property_long(pthreads_pool_entry, ZEND_STRL("last"), 0, ZEND_ACC_PROTECTED);
++
++	INIT_CLASS_ENTRY(ce, "Socket", pthreads_socket_methods);
++	pthreads_socket_entry = zend_register_internal_class_ex(&ce, pthreads_threaded_entry);
++	pthreads_socket_entry->create_object = pthreads_socket_ctor;
++
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("AF_UNIX"), AF_UNIX);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("AF_INET"), AF_INET);
++#ifdef HAVE_IPV6
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("AF_INET6"), AF_INET6);
++#endif
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOCK_STREAM"), SOCK_STREAM);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOCK_DGRAM"), SOCK_DGRAM);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOCK_RAW"), SOCK_RAW);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOCK_SEQPACKET"), SOCK_SEQPACKET);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOCK_RDM"), SOCK_RDM);
++
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_DEBUG"), SO_DEBUG);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_REUSEADDR"), SO_REUSEADDR);
++#ifdef SO_REUSEPORT
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_REUSEPORT"), SO_REUSEPORT);
++#endif
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_KEEPALIVE"), SO_KEEPALIVE);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_DONTROUTE"), SO_DONTROUTE);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_LINGER"), SO_LINGER);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_BROADCAST"), SO_BROADCAST);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_OOBINLINE"), SO_OOBINLINE);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_SNDBUF"), SO_SNDBUF);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_RCVBUF"), SO_RCVBUF);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_SNDLOWAT"), SO_SNDLOWAT);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_RCVLOWAT"), SO_RCVLOWAT);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_SNDTIMEO"), SO_SNDTIMEO);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_RCVTIMEO"), SO_RCVTIMEO);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_TYPE"), SO_TYPE);
++#ifdef SO_FAMILY
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_FAMILY"), SO_FAMILY);
++#endif
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_ERROR"), SO_ERROR);
++#ifdef SO_BINDTODEVICE
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SO_BINDTODEVICE"), SO_BINDTODEVICE);
++#endif
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOMAXCONN"), SOMAXCONN);
++#ifdef TCP_NODELAY
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("TCP_NODELAY"), TCP_NODELAY);
++#endif
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("NORMAL_READ"), PTHREADS_NORMAL_READ);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("BINARY_READ"), PTHREADS_BINARY_READ);
++
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOL_SOCKET"), SOL_SOCKET);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOL_TCP"), IPPROTO_TCP);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("SOL_UDP"), IPPROTO_UDP);
++
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_OOB"), MSG_OOB);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_CTRUNC"), MSG_CTRUNC);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_TRUNC"), MSG_TRUNC);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_PEEK"), MSG_PEEK);
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_DONTROUTE"), MSG_DONTROUTE);
++#ifdef MSG_EOR
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_EOR"), MSG_EOR);
++#endif
++#ifdef MSG_EOF
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_EOF"), MSG_EOF);
++#endif
++#ifdef MSG_CONFIRM
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_CONFIRM"), MSG_CONFIRM);
++#endif
++#ifdef MSG_ERRQUEUE
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_ERRQUEUE"), MSG_ERRQUEUE);
++#endif
++#ifdef MSG_NOSIGNAL
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_NOSIGNAL"), MSG_NOSIGNAL);
++#endif
++#ifdef MSG_MORE
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_MORE"), MSG_MORE);
++#endif
++#ifdef MSG_WAITFORONE
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_WAITFORONE"), MSG_WAITFORONE);
++#endif
++#ifdef MSG_CMSG_CLOEXEC
++	zend_declare_class_constant_long(pthreads_socket_entry, ZEND_STRL("MSG_CMSG_CLOEXEC"), MSG_CMSG_CLOEXEC);
++#endif
++
++#ifndef _WIN32
++#ifdef EPERM
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPERM"), EPERM);
++#endif
++#ifdef ENOENT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOENT"), ENOENT);
++#endif
++#ifdef EINTR
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EINTR"), EINTR);
++#endif
++#ifdef EIO
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EIO"), EIO);
++#endif
++#ifdef ENXIO
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENXIO"), ENXIO);
++#endif
++#ifdef E2BIG
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("E2BIG"), E2BIG);
++#endif
++#ifdef EBADF
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADF"), EBADF);
++#endif
++#ifdef EAGAIN
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EAGAIN"), EAGAIN);
++#endif
++#ifdef ENOMEM
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOMEM"), ENOMEM);
++#endif
++#ifdef EACCES
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EACCES"), EACCES);
++#endif
++#ifdef EFAULT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EFAULT"), EFAULT);
++#endif
++#ifdef ENOTBLK
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTBLK"), ENOTBLK);
++#endif
++#ifdef EBUSY
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBUSY"), EBUSY);
++#endif
++#ifdef EEXIST
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EEXIST"), EEXIST);
++#endif
++#ifdef EXDEV
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EXDEV"), EXDEV);
++#endif
++#ifdef ENODEV
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENODEV"), ENODEV);
++#endif
++#ifdef ENOTDIR
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTDIR"), ENOTDIR);
++#endif
++#ifdef EISDIR
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EISDIR"), EISDIR);
++#endif
++#ifdef EINVAL
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EINVAL"), EINVAL);
++#endif
++#ifdef ENFILE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENFILE"), ENFILE);
++#endif
++#ifdef EMFILE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EMFILE"), EMFILE);
++#endif
++#ifdef ENOTTY
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTTY"), ENOTTY);
++#endif
++#ifdef ENOSPC
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOSPC"), ENOSPC);
++#endif
++#ifdef ESPIPE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESPIPE"), ESPIPE);
++#endif
++#ifdef EROFS
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EROFS"), EROFS);
++#endif
++#ifdef EMLINK
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EMLINK"), EMLINK);
++#endif
++#ifdef EPIPE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPIPE"), EPIPE);
++#endif
++#ifdef ENAMETOOLONG
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENAMETOOLONG"), ENAMETOOLONG);
++#endif
++#ifdef ENOLCK
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOLCK"), ENOLCK);
++#endif
++#ifdef ENOSYS
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOSYS"), ENOSYS);
++#endif
++#ifdef ENOTEMPTY
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTEMPTY"), ENOTEMPTY);
++#endif
++#ifdef ELOOP
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ELOOP"), ELOOP);
++#endif
++#ifdef EWOULDBLOCK
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EWOULDBLOCK"), EWOULDBLOCK);
++#endif
++#ifdef ENOMSG
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOMSG"), ENOMSG);
++#endif
++#ifdef EIDRM
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EIDRM"), EIDRM);
++#endif
++#ifdef ECHRNG
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECHRNG"), ECHRNG);
++#endif
++#ifdef EL2NSYNC
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EL2NSYNC"), EL2NSYNC);
++#endif
++#ifdef EL3HLT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EL3HLT"), EL3HLT);
++#endif
++#ifdef EL3RST
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EL3RST"), EL3RST);
++#endif
++#ifdef ELNRNG
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ELNRNG"), ELNRNG);
++#endif
++#ifdef EUNATCH
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EUNATCH"), EUNATCH);
++#endif
++#ifdef ENOCSI
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOCSI"), ENOCSI);
++#endif
++#ifdef EL2HLT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EL2HLT"), EL2HLT);
++#endif
++#ifdef EBADE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADE"), EBADE);
++#endif
++#ifdef EBADR
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADR"), EBADR);
++#endif
++#ifdef EXFULL
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EXFULL"), EXFULL);
++#endif
++#ifdef ENOANO
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOANO"), ENOANO);
++#endif
++#ifdef EBADRQC
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADRQC"), EBADRQC);
++#endif
++#ifdef EBADSLT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADSLT"), EBADSLT);
++#endif
++#ifdef ENOSTR
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOSTR"), ENOSTR);
++#endif
++#ifdef ENODATA
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENODATA"), ENODATA);
++#endif
++#ifdef ETIME
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ETIME"), ETIME);
++#endif
++#ifdef ENOSR
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOSR"), ENOSR);
++#endif
++#ifdef ENONET
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENONET"), ENONET);
++#endif
++#ifdef EREMOTE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EREMOTE"), EREMOTE);
++#endif
++#ifdef ENOLINK
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOLINK"), ENOLINK);
++#endif
++#ifdef EADV
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EADV"), EADV);
++#endif
++#ifdef ESRMNT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESRMNT"), ESRMNT);
++#endif
++#ifdef ECOMM
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECOMM"), ECOMM);
++#endif
++#ifdef EPROTO
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPROTO"), EPROTO);
++#endif
++#ifdef EMULTIHOP
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EMULTIHOP"), EMULTIHOP);
++#endif
++#ifdef EBADMSG
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADMSG"), EBADMSG);
++#endif
++#ifdef ENOTUNIQ
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTUNIQ"), ENOTUNIQ);
++#endif
++#ifdef EBADFD
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADFD"), EBADFD);
++#endif
++#ifdef EREMCHG
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EREMCHG"), EREMCHG);
++#endif
++#ifdef ERESTART
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ERESTART"), ERESTART);
++#endif
++#ifdef ESTRPIPE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESTRPIPE"), ESTRPIPE);
++#endif
++#ifdef EUSERS
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EUSERS"), EUSERS);
++#endif
++#ifdef ENOTSOCK
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTSOCK"), ENOTSOCK);
++#endif
++#ifdef EDESTADDRREQ
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EDESTADDRREQ"), EDESTADDRREQ);
++#endif
++#ifdef EMSGSIZE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EMSGSIZE"), EMSGSIZE);
++#endif
++#ifdef EPROTOTYPE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPROTOTYPE"), EPROTOTYPE);
++#endif
++#ifdef ENOPROTOOPT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOPROTOOPT"), ENOPROTOOPT);
++#endif
++#ifdef EPROTONOSUPPORT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPROTONOSUPPORT"), EPROTONOSUPPORT);
++#endif
++#ifdef ESOCKTNOSUPPORT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESOCKTNOSUPPORT"), ESOCKTNOSUPPORT);
++#endif
++#ifdef EOPNOTSUPP
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EOPNOTSUPP"), EOPNOTSUPP);
++#endif
++#ifdef EPFNOSUPPORT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPFNOSUPPORT"), EPFNOSUPPORT);
++#endif
++#ifdef EAFNOSUPPORT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EAFNOSUPPORT"), EAFNOSUPPORT);
++#endif
++#ifdef EADDRINUSE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EADDRINUSE"), EADDRINUSE);
++#endif
++#ifdef EADDRNOTAVAIL
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EADDRNOTAVAIL"), EADDRNOTAVAIL);
++#endif
++#ifdef ENETDOWN
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENETDOWN"), ENETDOWN);
++#endif
++#ifdef ENETUNREACH
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENETUNREACH"), ENETUNREACH);
++#endif
++#ifdef ENETRESET
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENETRESET"), ENETRESET);
++#endif
++#ifdef ECONNABORTED
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECONNABORTED"), ECONNABORTED);
++#endif
++#ifdef ECONNRESET
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECONNRESET"), ECONNRESET);
++#endif
++#ifdef ENOBUFS
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOBUFS"), ENOBUFS);
++#endif
++#ifdef EISCONN
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EISCONN"), EISCONN);
++#endif
++#ifdef ENOTCONN
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTCONN"), ENOTCONN);
++#endif
++#ifdef ESHUTDOWN
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESHUTDOWN"), ESHUTDOWN);
++#endif
++#ifdef ETOOMANYREFS
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ETOOMANYREFS"), ETOOMANYREFS);
++#endif
++#ifdef ETIMEDOUT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ETIMEDOUT"), ETIMEDOUT);
++#endif
++#ifdef ECONNREFUSED
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECONNREFUSED"), ECONNREFUSED);
++#endif
++#ifdef EHOSTDOWN
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EHOSTDOWN"), EHOSTDOWN);
++#endif
++#ifdef EHOSTUNREACH
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EHOSTUNREACH"), EHOSTUNREACH);
++#endif
++#ifdef EALREADY
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EALREADY"), EALREADY);
++#endif
++#ifdef EINPROGRESS
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EINPROGRESS"), EINPROGRESS);
++#endif
++#ifdef EISNAM
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EISNAM"), EISNAM);
++#endif
++#ifdef EREMOTEIO
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EREMOTEIO"), EREMOTEIO);
++#endif
++#ifdef EDQUOT
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EDQUOT"), EDQUOT);
++#endif
++#ifdef ENOMEDIUM
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOMEDIUM"), ENOMEDIUM);
++#endif
++#ifdef EMEDIUMTYPE
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EMEDIUMTYPE"), EMEDIUMTYPE);
++#endif
++#else
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EINTR"), WSAEINTR);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EBADF"), WSAEBADF);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EACCES"), WSAEACCES);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EFAULT"), WSAEFAULT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EINVAL"), WSAEINVAL);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EMFILE"), WSAEMFILE);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EWOULDBLOCK"), WSAEWOULDBLOCK);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EINPROGRESS"), WSAEINPROGRESS);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EALREADY"), WSAEALREADY);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTSOCK"), WSAENOTSOCK);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EDESTADDRREQ"), WSAEDESTADDRREQ);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EMSGSIZE"), WSAEMSGSIZE);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPROTOTYPE"), WSAEPROTOTYPE);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOPROTOOPT"), WSAENOPROTOOPT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPROTONOSUPPORT"), WSAEPROTONOSUPPORT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESOCKTNOSUPPORT"), WSAESOCKTNOSUPPORT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EOPNOTSUPP"), WSAEOPNOTSUPP);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPFNOSUPPORT"), WSAEPFNOSUPPORT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EAFNOSUPPORT"), WSAEAFNOSUPPORT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EADDRINUSE"), WSAEADDRINUSE);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EADDRNOTAVAIL"), WSAEADDRNOTAVAIL);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENETDOWN"), WSAENETDOWN);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENETUNREACH"), WSAENETUNREACH);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENETRESET"), WSAENETRESET);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECONNABORTED"), WSAECONNABORTED);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECONNRESET"), WSAECONNRESET);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOBUFS"), WSAENOBUFS);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EISCONN"), WSAEISCONN);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTCONN"), WSAENOTCONN);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESHUTDOWN"), WSAESHUTDOWN);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ETOOMANYREFS"), WSAETOOMANYREFS);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ETIMEDOUT"), WSAETIMEDOUT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ECONNREFUSED"), WSAECONNREFUSED);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ELOOP"), WSAELOOP);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENAMETOOLONG"), WSAENAMETOOLONG);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EHOSTDOWN"), WSAEHOSTDOWN);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EHOSTUNREACH"), WSAEHOSTUNREACH);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ENOTEMPTY"), WSAENOTEMPTY);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EPROCLIM"), WSAEPROCLIM);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EUSERS"), WSAEUSERS);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EDQUOT"), WSAEDQUOT);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("ESTALE"), WSAESTALE);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EREMOTE"), WSAEREMOTE);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("EDISCON"), WSAEDISCON);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("SYSNOTREADY"), WSASYSNOTREADY);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("VERNOTSUPPORTED"), WSAVERNOTSUPPORTED);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("NOTINITIALISED"), WSANOTINITIALISED);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("HOST_NOT_FOUND"), WSAHOST_NOT_FOUND);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("TRY_AGAIN"), WSATRY_AGAIN);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("NO_RECOVERY"), WSANO_RECOVERY);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("NO_DATA"), WSANO_DATA);
++	zend_declare_class_constant_long(pthreads_socket_entry,  ZEND_STRL("NO_ADDRESS"), WSANO_ADDRESS);
++#endif
++
++	/*
++	* Setup object handlers
++	*/
++	zend_handlers = (zend_object_handlers*)zend_get_std_object_handlers();
++	
++	memcpy(&pthreads_handlers, zend_handlers, sizeof(zend_object_handlers));
++
++	pthreads_handlers.offset = XtOffsetOf(pthreads_object_t, std);
++
++	pthreads_handlers.free_obj = pthreads_base_free;
++	pthreads_handlers.cast_object = pthreads_cast_object;
++	pthreads_handlers.count_elements = pthreads_count_properties;
++
++	pthreads_handlers.get_debug_info = pthreads_read_debug;	
++	pthreads_handlers.get_properties = pthreads_read_properties;
++	
++	pthreads_handlers.read_property = pthreads_read_property;
++	pthreads_handlers.write_property = pthreads_write_property;
++	pthreads_handlers.has_property = pthreads_has_property;
++	pthreads_handlers.unset_property = pthreads_unset_property;
++	
++	pthreads_handlers.read_dimension = pthreads_read_dimension;
++	pthreads_handlers.write_dimension = pthreads_write_dimension;
++	pthreads_handlers.has_dimension = pthreads_has_dimension;
++	pthreads_handlers.unset_dimension = pthreads_unset_dimension;
++
++	pthreads_handlers.get_property_ptr_ptr = NULL;
++	pthreads_handlers.get = NULL;
++	pthreads_handlers.set = NULL;
++	pthreads_handlers.get_gc = pthreads_base_gc;
++
++	pthreads_handlers.clone_obj = pthreads_base_clone;
++	pthreads_handlers.compare_objects = pthreads_compare_objects;
++
++	memcpy(&pthreads_socket_handlers, &pthreads_handlers, sizeof(zend_object_handlers));
++
++	pthreads_socket_handlers.count_elements = pthreads_count_properties_disallow;
++
++	pthreads_socket_handlers.cast_object = zend_handlers->cast_object;
++	pthreads_socket_handlers.get_properties = pthreads_read_properties_disallow;
++	pthreads_socket_handlers.read_property = pthreads_read_property_disallow;
++	pthreads_socket_handlers.write_property = pthreads_write_property_disallow;
++	pthreads_socket_handlers.has_property = pthreads_has_property_disallow;
++	pthreads_socket_handlers.unset_property = pthreads_unset_property_disallow;
++	pthreads_socket_handlers.read_dimension = pthreads_read_dimension_disallow;
++	pthreads_socket_handlers.write_dimension = pthreads_write_dimension_disallow;
++	pthreads_socket_handlers.has_dimension = pthreads_has_dimension_disallow;
++	pthreads_socket_handlers.unset_dimension = pthreads_unset_dimension_disallow;
++
++	ZEND_INIT_MODULE_GLOBALS(pthreads, pthreads_globals_ctor, NULL);	
++
++	if (pthreads_globals_init()) {
++		TSRMLS_CACHE_UPDATE();
++		
++		/*
++		* Global Init
++		*/
++		pthreads_instance = TSRMLS_CACHE;
++	}
++
++	zend_set_user_opcode_handler(ZEND_RECV, php_pthreads_recv);
++	zend_set_user_opcode_handler(ZEND_VERIFY_RETURN_TYPE, php_pthreads_verify_return_type);
++
++	return SUCCESS;
++}
++
++static inline int sapi_cli_deactivate(void) 
++{
++	fflush(stdout);
++	if (SG(request_info).argv0) {
++		free(SG(request_info).argv0);
++		SG(request_info).argv0 = NULL;
++	}
++	return SUCCESS;
++}
++
++PHP_MSHUTDOWN_FUNCTION(pthreads)
++{
++	if (pthreads_instance == TSRMLS_CACHE) {
++		pthreads_globals_shutdown();
++
++		if (memcmp(sapi_module.name, ZEND_STRL("cli")) == SUCCESS) {
++			sapi_module.deactivate = sapi_cli_deactivate;
++		}
++	}
++
++	zend_execute_ex = zend_execute_ex_hook;
++
++	return SUCCESS;
++}
++
++ZEND_MODULE_POST_ZEND_DEACTIVATE_D(pthreads)
++{
++	if (PTHREADS_ZG(resources)) {
++		zend_hash_destroy(PTHREADS_ZG(resources));
++		FREE_HASHTABLE(PTHREADS_ZG(resources));
++		PTHREADS_ZG(resources) = NULL;
++	}
++
++	return SUCCESS;
++}
++
++PHP_RINIT_FUNCTION(pthreads) {
++	ZEND_TSRMLS_CACHE_UPDATE();
++
++	zend_hash_init(&PTHREADS_ZG(resolve), 15, NULL, NULL, 0);
++	zend_hash_init(&PTHREADS_ZG(filenames), 15, NULL, NULL, 0);
++
++	PTHREADS_ZG(hard_copy_interned_strings) = 0;
++
++	if (pthreads_instance != TSRMLS_CACHE) {
++		if (memcmp(sapi_module.name, ZEND_STRL("cli")) == SUCCESS) {
++			sapi_module.deactivate = NULL;
++		}
++	}
++
++	return SUCCESS;
++}
++
++PHP_RSHUTDOWN_FUNCTION(pthreads) {
++	zend_hash_destroy(&PTHREADS_ZG(resolve));
++	zend_hash_destroy(&PTHREADS_ZG(filenames));
++
++	return SUCCESS;
++}
++
++PHP_MINFO_FUNCTION(pthreads)
++{
++	php_info_print_table_start();
++	php_info_print_table_row(2, "Version", PHP_PTHREADS_VERSION);
++	php_info_print_table_end();
++}
++
++#ifndef HAVE_PTHREADS_CLASS_THREADED
++#	include <classes/threaded.h>
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_THREAD
++#	include <classes/thread.h>
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_COLLECTABLE
++#	include <classes/collectable.h>
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_WORKER
++#	include <classes/worker.h>
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_POOL
++#	include <classes/pool.h>
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_SOCKET
++#	include <classes/socket.h>
++#endif
++
++#endif
+diff --git a/ext/pthreads/php_pthreads.h b/ext/pthreads/php_pthreads.h
+new file mode 100644
+index 00000000..edd14b8a
+--- /dev/null
++++ b/ext/pthreads/php_pthreads.h
+@@ -0,0 +1,57 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2014                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <joe.watkins@live.co.uk>                         |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PHP_PTHREADS_H
++#define HAVE_PHP_PTHREADS_H
++#define PHP_PTHREADS_EXTNAME "pthreads"
++#define PHP_PTHREADS_VERSION "3.2.1dev"
++
++PHP_MINIT_FUNCTION(pthreads);
++PHP_MSHUTDOWN_FUNCTION(pthreads);
++PHP_RINIT_FUNCTION(pthreads);
++PHP_RSHUTDOWN_FUNCTION(pthreads);
++PHP_MINFO_FUNCTION(pthreads);
++ZEND_MODULE_POST_ZEND_DEACTIVATE_D(pthreads);
++
++#ifndef HAVE_PTHREADS_CLASS_THREADED_H
++#	include "classes/threaded.h"
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_THREAD_H
++#	include "classes/thread.h"
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_COLLECTABLE_H
++#	include "classes/collectable.h"
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_WORKER_H
++#	include "classes/worker.h"
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_POOL_H
++#	include "classes/pool.h"
++#endif
++
++#ifndef HAVE_PTHREADS_CLASS_SOCKET_H
++#	include "classes/socket.h"
++#endif
++
++extern zend_module_entry pthreads_module_entry;
++#define phpext_pthreads_ptr &pthreads_module_entry
++
++#endif 
+diff --git a/ext/pthreads/src/copy.h b/ext/pthreads/src/copy.h
+new file mode 100644
+index 00000000..af79c2d5
+--- /dev/null
++++ b/ext/pthreads/src/copy.h
+@@ -0,0 +1,410 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_COPY_H
++#define HAVE_PTHREADS_COPY_H
++
++/* {{{ */
++static HashTable* pthreads_copy_statics(HashTable *old) {
++	HashTable *statics = NULL;
++	
++	if (old) {
++		zend_string *key;
++		zval *value;
++
++		ALLOC_HASHTABLE(statics);
++		zend_hash_init(statics,
++			zend_hash_num_elements(old),
++			NULL, ZVAL_PTR_DTOR, 0);
++
++		ZEND_HASH_FOREACH_STR_KEY_VAL(old, key, value) {
++			zend_string *name = zend_string_new(key);
++			zval *next = value;
++			while (Z_TYPE_P(next) == IS_REFERENCE)
++				next = &Z_REF_P(next)->val;
++
++			if (Z_REFCOUNTED_P(next)) {
++				zval copy;
++
++				switch (Z_TYPE_P(next)) {
++					case IS_STRING:
++						ZVAL_STR(&copy, 
++							zend_string_new(Z_STR_P(next)));
++						zend_hash_add(statics, name, &copy);
++					break;
++					
++					case IS_OBJECT:
++						if (instanceof_function(Z_OBJCE_P(next), pthreads_threaded_entry) ||
++							instanceof_function(Z_OBJCE_P(next), zend_ce_closure)) {
++							pthreads_store_separate(next, &copy, 1);
++							zend_hash_add(statics, name, &copy);
++						} else zend_hash_add_empty_element(statics, name);
++					break;
++
++					case IS_ARRAY:
++						pthreads_store_separate(next, &copy, 1);
++						zend_hash_add(statics, name, &copy);
++					break;
++
++					case IS_CONSTANT_AST:
++#if PHP_VERSION_ID < 70300
++						ZVAL_NEW_AST(&copy, zend_ast_copy(Z_AST_P(next)->ast));
++#else
++						ZVAL_AST(&copy, zend_ast_copy(GC_AST(Z_AST_P(next))));
++#endif
++						zend_hash_add(statics, name, &copy);
++					break;
++
++					default:
++						zend_hash_add_empty_element(statics, name);					
++				}
++			} else zend_hash_add(statics, name, next);
++			zend_string_release(name);
++		} ZEND_HASH_FOREACH_END();
++	}
++	
++	return statics;
++} /* }}} */
++
++/* {{{ */
++static zend_string** pthreads_copy_variables(zend_string **old, int end) {
++	zend_string **variables = safe_emalloc(end, sizeof(zend_string*), 0);
++	int it = 0;
++	
++	while (it < end) {
++		variables[it] = 
++			zend_string_new(old[it]);
++		it++;
++	}
++	
++	return variables;
++} /* }}} */
++
++/* {{{ */
++static zend_try_catch_element* pthreads_copy_try(zend_try_catch_element *old, int end) {	
++	zend_try_catch_element *try_catch = safe_emalloc(end, sizeof(zend_try_catch_element), 0);
++	
++	memcpy(
++		try_catch, 
++		old,
++		sizeof(zend_try_catch_element) * end);
++	
++	return try_catch;
++} /* }}} */
++
++/* {{{ */
++static zend_live_range* pthreads_copy_live(zend_live_range *old, int end) { 
++	zend_live_range *range = safe_emalloc(end, sizeof(zend_live_range), 0);
++
++	memcpy(
++		range,
++		old,
++		sizeof(zend_live_range) * end);
++
++	return range;
++} /* }}} */
++
++/* {{{ */
++static zval* pthreads_copy_literals(zval *old, int last) {
++	zval *literals = (zval*) safe_emalloc(last, sizeof(zval), 0);
++	zval *literal = literals,
++		 *end = literals + last;
++
++	memcpy(literals, old, sizeof(zval) * last);	
++
++	while (literal < end) {
++		switch (Z_TYPE_P(literal)) {
++			case IS_ARRAY:
++				pthreads_store_separate(literal, literal, 1);
++			break;
++#if PHP_VERSION_ID < 70300
++			case IS_CONSTANT:
++#endif
++			case IS_CONSTANT_AST:
++				zval_copy_ctor(literal);
++			break;
++
++		}
++		literal++;
++	}
++	
++	return literals;
++} /* }}} */
++
++#if PHP_VERSION_ID < 70300
++/* {{{ */
++static zend_op* pthreads_copy_opcodes(zend_op_array *op_array, zval *literals) {
++	zend_op *copy = safe_emalloc(
++		op_array->last, sizeof(zend_op), 0);
++
++	memcpy(copy, op_array->opcodes, sizeof(zend_op) * op_array->last);
++
++	/* The following code comes from ext/opcache/zend_persist.c */
++#if ZEND_USE_ABS_CONST_ADDR || ZEND_USE_ABS_JMP_ADDR
++	zend_op *opline = copy;
++	zend_op *end    = copy + op_array->last;
++
++	for (; opline < end; opline++) {
++#if ZEND_USE_ABS_CONST_ADDR
++		if (opline->op1_type == IS_CONST)
++			opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)literals));
++		if (opline->op2_type == IS_CONST) 
++			opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)literals));
++#endif
++#if ZEND_USE_ABS_JMP_ADDR
++		if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
++			/* fix jumps to point to new array */
++			switch (opline->opcode) {
++				case ZEND_JMP:
++				case ZEND_FAST_CALL:
++					opline->op1.jmp_addr = &copy[opline->op1.jmp_addr - op_array->opcodes];
++					break;
++				case ZEND_JMPZNZ:
++					/* relative extended_value don't have to be changed */
++					/* break omitted intentionally */
++				case ZEND_JMPZ:
++				case ZEND_JMPNZ:
++				case ZEND_JMPZ_EX:
++				case ZEND_JMPNZ_EX:
++				case ZEND_JMP_SET:
++				case ZEND_COALESCE:
++				case ZEND_FE_RESET_R:
++				case ZEND_FE_RESET_RW:
++				case ZEND_ASSERT_CHECK:
++					opline->op2.jmp_addr = &copy[opline->op2.jmp_addr - op_array->opcodes];
++					break;
++				case ZEND_DECLARE_ANON_CLASS:
++				case ZEND_DECLARE_ANON_INHERITED_CLASS:
++				case ZEND_FE_FETCH_R:
++				case ZEND_FE_FETCH_RW:
++				case ZEND_SWITCH_LONG:
++				case ZEND_SWITCH_STRING:
++					/* relative extended_value don't have to be changed */
++					break;
++			}
++		}
++#endif
++	}
++#endif
++
++		
++
++	return copy;
++} /* }}} */
++
++#else /* PHP_VERSION_ID >= 70300 */
++
++/* {{{ */
++static zend_op* pthreads_copy_opcodes(zend_op_array *op_array, zval *literals) {
++	zend_op *copy = safe_emalloc(
++		op_array->last, sizeof(zend_op), 0);
++
++	memcpy(copy, op_array->opcodes, sizeof(zend_op) * op_array->last);
++
++	/* The following code comes from ext/opcache/zend_persist.c */
++	zend_op *opline = copy;
++	zend_op *end    = copy + op_array->last;
++
++	for (; opline < end; opline++) {
++#if ZEND_USE_ABS_CONST_ADDR
++			if (opline->op1_type == IS_CONST) {
++				opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)literals));
++			}
++			if (opline->op2_type == IS_CONST) {
++				opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)literals));
++			}
++#else
++			if (opline->op1_type == IS_CONST) {
++				opline->op1.constant =
++					(char*)(op_array->literals +
++						((zval*)((char*)(op_array->opcodes + (opline - copy)) +
++						(int32_t)opline->op1.constant) - literals)) -
++					(char*)opline;
++			}
++			if (opline->op2_type == IS_CONST) {
++				opline->op2.constant =
++					(char*)(op_array->literals +
++						((zval*)((char*)(op_array->opcodes + (opline - copy)) +
++						(int32_t)opline->op2.constant) - literals)) -
++					(char*)opline;
++			}
++#endif
++#if ZEND_USE_ABS_JMP_ADDR
++		if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
++			/* fix jumps to point to new array */
++			switch (opline->opcode) {
++				case ZEND_JMP:
++				case ZEND_FAST_CALL:
++					opline->op1.jmp_addr = &copy[opline->op1.jmp_addr - op_array->opcodes];
++					break;
++				case ZEND_JMPZNZ:
++					/* relative extended_value don't have to be changed */
++					/* break omitted intentionally */
++				case ZEND_JMPZ:
++				case ZEND_JMPNZ:
++				case ZEND_JMPZ_EX:
++				case ZEND_JMPNZ_EX:
++				case ZEND_JMP_SET:
++				case ZEND_COALESCE:
++				case ZEND_FE_RESET_R:
++				case ZEND_FE_RESET_RW:
++				case ZEND_ASSERT_CHECK:
++					opline->op2.jmp_addr = &copy[opline->op2.jmp_addr - op_array->opcodes];
++					break;
++				case ZEND_DECLARE_ANON_CLASS:
++				case ZEND_DECLARE_ANON_INHERITED_CLASS:
++				case ZEND_FE_FETCH_R:
++				case ZEND_FE_FETCH_RW:
++				case ZEND_SWITCH_LONG:
++				case ZEND_SWITCH_STRING:
++					/* relative extended_value don't have to be changed */
++					break;
++			}
++		}
++#endif
++	}
++
++	return copy;
++} /* }}} */
++#endif /* PHP_VERSION_ID < 70300 */
++
++/* {{{ */
++static zend_arg_info* pthreads_copy_arginfo(zend_op_array *op_array, zend_arg_info *old, uint32_t end) {
++	zend_arg_info *info;
++	uint32_t it = 0;
++
++	if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
++		old--;
++		end++;
++	}
++
++	if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
++		end++;
++	}
++
++	info = safe_emalloc
++		(end, sizeof(zend_arg_info), 0);
++	memcpy(info, old, sizeof(zend_arg_info) * end);	
++
++	while (it < end) {
++		if (info[it].name)
++			info[it].name = zend_string_new(old[it].name);
++
++		if (ZEND_TYPE_IS_SET(old[it].type) && ZEND_TYPE_IS_CLASS(old[it].type)) {
++			info[it].type = ZEND_TYPE_ENCODE_CLASS(
++				zend_string_new(
++					ZEND_TYPE_NAME(info[it].type)), 
++				ZEND_TYPE_ALLOW_NULL(info[it].type));
++		}
++		it++;
++	}
++	
++	if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
++		info++;
++	}
++	
++	return info;
++} /* }}} */
++
++/* {{{ */
++static inline zend_function* pthreads_copy_user_function(zend_function *function) {
++	zend_function  *copy;
++	zend_op_array  *op_array;
++	zend_string   **variables, *filename_copy;
++	zval           *literals;
++	zend_arg_info  *arg_info;
++	
++	copy = (zend_function*)
++		zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
++	memcpy(copy, function, sizeof(zend_op_array));
++	
++	op_array = &copy->op_array;
++	variables = op_array->vars;
++	literals = op_array->literals;
++	arg_info = op_array->arg_info;
++
++	op_array->function_name = zend_string_new(op_array->function_name);
++	/* we don't care about prototypes */
++	op_array->prototype = NULL;
++	op_array->refcount = emalloc(sizeof(uint32_t));
++	(*op_array->refcount) = 1;
++	/* we never want to share the same runtime cache */
++	op_array->run_time_cache = NULL;
++#if PHP_VERSION_ID >= 70300
++	op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO;
++#endif
++
++	if (op_array->doc_comment) {
++		op_array->doc_comment = zend_string_new(op_array->doc_comment);
++	}
++	
++	if (!(filename_copy = zend_hash_find_ptr(&PTHREADS_ZG(filenames), op_array->filename))) {
++		filename_copy = zend_string_new(op_array->filename);
++		zend_hash_add_ptr(&PTHREADS_ZG(filenames), filename_copy, filename_copy);
++		zend_string_release(filename_copy);
++	}
++
++	op_array->filename = filename_copy;
++
++	if (op_array->literals) op_array->literals = pthreads_copy_literals (literals, op_array->last_literal);
++
++	op_array->opcodes = pthreads_copy_opcodes(op_array, literals);
++
++	if (op_array->arg_info) 	op_array->arg_info = pthreads_copy_arginfo(op_array, arg_info, op_array->num_args);
++	if (op_array->live_range)		op_array->live_range = pthreads_copy_live(op_array->live_range, op_array->last_live_range);
++	if (op_array->try_catch_array)  op_array->try_catch_array = pthreads_copy_try(op_array->try_catch_array, op_array->last_try_catch);
++	if (op_array->vars) 		op_array->vars = pthreads_copy_variables(variables, op_array->last_var);
++	if (op_array->static_variables) op_array->static_variables = pthreads_copy_statics(op_array->static_variables);
++
++	return copy;
++} /* }}} */
++
++/* {{{ */
++static inline zend_function* pthreads_copy_internal_function(zend_function *function) {
++	zend_function *copy = zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
++	memcpy(copy, function, sizeof(zend_internal_function));
++	copy->common.fn_flags |= ZEND_ACC_ARENA_ALLOCATED;
++	return copy;
++} /* }}} */
++
++/* {{{ */
++static zend_function* pthreads_copy_function(zend_function *function) {
++	zend_function *copy;
++
++	if (!(function->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
++		copy = zend_hash_index_find_ptr(&PTHREADS_ZG(resolve), (zend_ulong)function);
++
++		if (copy) {
++			function_add_ref(copy);
++			return copy;
++		}
++	}
++	
++	if (function->type == ZEND_USER_FUNCTION) {
++		copy = pthreads_copy_user_function(function);
++	} else {
++		copy = pthreads_copy_internal_function(function);
++	}
++
++	if (function->op_array.fn_flags & ZEND_ACC_CLOSURE) { //don't cache closures
++		return copy;
++	}
++
++	return zend_hash_index_update_ptr(&PTHREADS_ZG(resolve), (zend_ulong) function, copy);
++} /* }}} */
++#endif
++
+diff --git a/ext/pthreads/src/globals.c b/ext/pthreads/src/globals.c
+new file mode 100644
+index 00000000..ca66dd50
+--- /dev/null
++++ b/ext/pthreads/src/globals.c
+@@ -0,0 +1,170 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_GLOBALS
++#define HAVE_PTHREADS_GLOBALS
++
++#ifndef HAVE_PTHREADS_GLOBALS_H
++#	include <src/globals.h>
++#endif
++
++#ifndef HAVE_PTHREADS_PREPARE_H
++#	include <src/prepare.h>
++#endif
++
++struct _pthreads_globals pthreads_globals;
++
++#ifndef PTHREADS_G
++#	define PTHREADS_G () ?  : (void***) &pthreads_globals
++#endif
++
++extern int pthreads_connect(pthreads_object_t* source, pthreads_object_t* destination);
++
++/* {{{ */
++zend_bool pthreads_globals_init(){
++	if (!PTHREADS_G(init)&&!PTHREADS_G(failed)) {
++		PTHREADS_G(init)=1;
++		if (!(PTHREADS_G(monitor)=pthreads_monitor_alloc()))
++			PTHREADS_G(failed)=1;
++		if (PTHREADS_G(failed)) {
++		    PTHREADS_G(init)=0;
++		} else {
++		    zend_hash_init(
++		    	&PTHREADS_G(objects), 64, NULL, (dtor_func_t) NULL, 1);
++		}
++
++#define INIT_STRING(n, v) do { \
++	PTHREADS_G(strings).n = zend_new_interned_string(zend_string_init(v, 1)); \
++} while(0)
++
++		INIT_STRING(run, ZEND_STRL("run"));
++		INIT_STRING(session.cache_limiter, ZEND_STRL("cache_limiter"));
++		INIT_STRING(session.use_cookies, ZEND_STRL("use_cookies"));
++#undef INIT_STRING
++
++		ZVAL_INTERNED_STR(
++			&PTHREADS_G(strings).worker, 
++			zend_new_interned_string(zend_string_init(ZEND_STRL("worker"), 1)));
++
++		return PTHREADS_G(init);
++	} else return 0;
++} /* }}} */
++
++/* {{{ */
++zend_bool pthreads_globals_lock(){
++	return pthreads_monitor_lock(PTHREADS_G(monitor));
++} /* }}} */
++
++/* {{{ */
++void pthreads_globals_unlock() {
++	pthreads_monitor_unlock(PTHREADS_G(monitor));
++} /* }}} */
++
++/* {{{ */
++void* pthreads_globals_object_alloc(size_t length) {
++	void *bucket     = (void*) ecalloc(1, length);
++
++	if (pthreads_globals_lock()) {
++		zend_hash_index_update_ptr(
++			&PTHREADS_G(objects),
++			(zend_ulong) bucket, bucket);
++		pthreads_globals_unlock();
++	}
++
++	memset(bucket, 0, length);
++	
++	return bucket;
++} /* }}} */
++
++/* {{{ */
++zend_bool pthreads_globals_object_connect(zend_ulong address, zend_class_entry *ce, zval *object) {
++	zend_bool valid = 0;
++
++	if (!address)
++		return valid;
++
++	if (pthreads_globals_lock()) {
++		if (zend_hash_index_exists(&PTHREADS_G(objects), address)) {
++			valid = 1;
++		}
++		pthreads_globals_unlock();
++	}
++
++	if (valid) {
++		pthreads_object_t *pthreads = (pthreads_object_t*) address;
++
++		/*
++		* This can be done outside of a critical section because there are only two possibilities:
++		*	We own the object: no possible pathway to fault (read free'd memory)
++		*	We don't own the object: possibly pathway to fault whether we use critical section or not:
++		*		We use a critical section: we create the connection knowing that address cannot be freed while doing so
++		*		however, as soon as we leave the section, and before the conext that called this routine can reference the connection
++		*		object the creating context may have free'd the object.
++		*		We don't use a critical section: the object may be freed while we are creating the connection, causing a fault.
++		* 
++		* As always, it's necessary for the programmer to retain the appropriate references so that this does not fault, creating connections
++		* in a critical section would be unecessarily slow, not to mention recursively lock mutex (which is fine, but not ideal).
++		*/
++
++		if (PTHREADS_IN_CREATOR(pthreads)) {
++			/* we own the object in this context */
++			ZVAL_OBJ(object, &pthreads->std);
++			Z_ADDREF_P(object);
++		} else {
++			/* we do not own the object, create a connection */
++			if (!ce) {
++				/* we may not know the class, can't use ce directly
++					from zend_object because it is from another context */
++				PTHREADS_ZG(hard_copy_interned_strings) = 1;
++				ce = pthreads_prepared_entry(pthreads, pthreads->std.ce);
++				PTHREADS_ZG(hard_copy_interned_strings) = 0;
++			}
++			object_init_ex(object, ce);
++			pthreads_connect(pthreads, PTHREADS_FETCH_FROM(Z_OBJ_P(object)));
++		}
++	}
++	
++	return valid;
++} /* }}} */
++
++
++/* {{{ */
++zend_bool pthreads_globals_object_delete(void *address) {
++	zend_bool deleted = 0;
++	
++	if (!address)
++		return deleted;
++
++	if (pthreads_globals_lock()) {
++		deleted = zend_hash_index_del(
++			&PTHREADS_G(objects), (zend_ulong) address);
++		pthreads_globals_unlock();
++	}
++	
++	return deleted;
++} /* }}} */
++
++/* {{{ */
++void pthreads_globals_shutdown() {
++	if (PTHREADS_G(init)) {
++		PTHREADS_G(init)=0;
++		PTHREADS_G(failed)=0;
++		/* we allow proc shutdown to destroy tables, and global strings */
++		pthreads_monitor_free(PTHREADS_G(monitor));
++	}
++} /* }}} */
++#endif
+diff --git a/ext/pthreads/src/globals.h b/ext/pthreads/src/globals.h
+new file mode 100644
+index 00000000..c9a51888
+--- /dev/null
++++ b/ext/pthreads/src/globals.h
+@@ -0,0 +1,97 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_GLOBALS_H
++#define HAVE_PTHREADS_GLOBALS_H
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++/* {{{ pthreads_globals */
++struct _pthreads_globals {
++	/*
++	* Initialized flag
++	*/
++	volatile zend_bool init;
++	
++	/*
++	* Failed flag
++	*/
++	zend_bool failed;
++	
++	/*
++	* Global Monitor
++	*/
++	pthreads_monitor_t *monitor;
++
++	/*
++	* Global/Default Resource Destructor
++	*/
++	dtor_func_t (default_resource_dtor);
++	
++	/*
++	* Objects Cache
++	*/
++	HashTable objects;
++	
++	/*
++	* High Frequency Strings
++	*/
++	struct _strings {
++		zend_string *run;
++		zval         worker;
++		struct _session {
++			zend_string *cache_limiter;
++			zend_string *use_cookies;
++		} session;
++	} strings;
++}; /* }}} */
++
++extern struct _pthreads_globals pthreads_globals;
++
++ZEND_EXTERN_MODULE_GLOBALS(pthreads)
++
++/* {{{ PTHREADS_G */
++#define PTHREADS_G(v) pthreads_globals.v
++/* }}} */
++
++/* {{{ */
++zend_bool pthreads_globals_object_delete(void *address); /* }}} */
++
++/* {{{ */
++zend_bool pthreads_globals_object_connect(zend_ulong address, zend_class_entry *ce, zval *object); /* }}} */
++
++/* {{{ */
++void* pthreads_globals_object_alloc(size_t length); /* }}} */
++
++/* {{{ initialize (true) globals */
++zend_bool pthreads_globals_init(); /* }}} */
++
++/* {{{ acquire global lock */
++zend_bool pthreads_globals_lock(); /* }}} */
++
++/* {{{ release global lock */
++void pthreads_globals_unlock(); /* }}} */
++
++/* {{{ copy string to globals */
++char *pthreads_global_string(char *strkey, int32_t keylen, zend_bool lower); /* }}} */
++
++/* {{{ shutdown global structures */
++void pthreads_globals_shutdown(); /* }}} */
++
++#endif /* HAVE_PTHREADS_GLOBAL_H */
+diff --git a/ext/pthreads/src/handlers.c b/ext/pthreads/src/handlers.c
+new file mode 100644
+index 00000000..dc1acfc9
+--- /dev/null
++++ b/ext/pthreads/src/handlers.c
+@@ -0,0 +1,380 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_HANDLERS
++#define HAVE_PTHREADS_HANDLERS
++
++#ifndef HAVE_PTHREADS_HANDLERS_H
++#	include <src/handlers.h>
++#endif
++
++#ifndef HAVE_PTHREADS_OBJECT_H
++#	include <src/object.h>
++#endif
++
++#ifndef HAVE_PTHREADS_GLOBALS_H
++#	include <src/globals.h>
++#endif
++
++#define IN_GET      (1<<0)
++#define IN_SET      (1<<1)
++#define IN_UNSET    (1<<2)
++#define IN_ISSET    (1<<3)
++
++typedef uint32_t zend_guard;
++#define pthreads_get_guard(o, m) \
++	((Z_TYPE_P(m) == IS_STRING) ? zend_get_property_guard(o, Z_STR_P(m)) : NULL)
++/* }}} */
++
++/* {{{ */
++int pthreads_count_properties(PTHREADS_COUNT_PASSTHRU_D) {
++	return pthreads_store_count(PTHREADS_COUNT_PASSTHRU_C);
++} /* }}} */
++
++/* {{{ */
++int pthreads_count_properties_disallow(PTHREADS_COUNT_PASSTHRU_D) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++		"%s objects are not allowed to have properties",
++		ZSTR_VAL(threaded->std.ce->name));
++
++	return -1;
++} /* }}} */
++
++/* {{{ */
++HashTable* pthreads_read_debug(PTHREADS_READ_DEBUG_PASSTHRU_D) {
++	HashTable *table = emalloc(sizeof(HashTable));
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	zend_hash_init(table, 8, NULL, ZVAL_PTR_DTOR, 0);
++	*is_temp = 1;
++
++	if (!PTHREADS_IS_SOCKET(threaded)) {
++		pthreads_store_tohash(object, table);
++	}
++
++	return table;
++} /* }}} */
++
++/* {{{ */
++HashTable* pthreads_read_properties(PTHREADS_READ_PROPERTIES_PASSTHRU_D) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++
++	pthreads_store_tohash(
++		object, threaded->std.properties);
++		
++	return threaded->std.properties;
++} /* }}} */
++
++/* {{{ */
++HashTable* pthreads_read_properties_disallow(PTHREADS_READ_PROPERTIES_PASSTHRU_D) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++
++	zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++		"%s objects are not allowed to have properties",
++		ZSTR_VAL(threaded->std.ce->name));
++
++	return threaded->std.properties;
++} /* }}} */
++
++/* {{{ */
++zval * pthreads_read_property (PTHREADS_READ_PROPERTY_PASSTHRU_D) {
++	zend_guard *guard = NULL;
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++
++	if (Z_OBJCE_P(object)->__get && (guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_GET)) {
++		zend_fcall_info fci = empty_fcall_info;
++        zend_fcall_info_cache fcc = empty_fcall_info_cache;
++		
++		fci.size = sizeof(zend_fcall_info);
++		fci.retval = rv;
++		fci.object = &threaded->std;
++		zend_fcall_info_argn(&fci, 1, member);
++#if PHP_VERSION_ID < 70300
++		fcc.initialized = 1;
++#endif
++		fcc.function_handler = Z_OBJCE_P(object)->__get;
++		fcc.object = &threaded->std;
++		
++		(*guard) |= IN_GET;
++		zend_call_function(&fci, &fcc);
++		(*guard) &= ~IN_GET;
++
++		zend_fcall_info_args_clear(&fci, 1);
++	} else {
++		pthreads_store_read(object, member, type, rv);
++	}
++	
++	return rv;
++} 
++
++zval* pthreads_read_dimension(PTHREADS_READ_DIMENSION_PASSTHRU_D) { return pthreads_read_property(PTHREADS_READ_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++zval * pthreads_read_property_disallow (PTHREADS_READ_PROPERTY_PASSTHRU_D) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++		"%s objects are not allowed to have properties",
++		ZSTR_VAL(threaded->std.ce->name));
++	
++	return &EG(uninitialized_zval);
++} 
++
++zval* pthreads_read_dimension_disallow(PTHREADS_READ_DIMENSION_PASSTHRU_D) { return pthreads_read_property_disallow(PTHREADS_READ_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++void pthreads_write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_D) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++
++	switch(Z_TYPE_P(value)){
++		case IS_UNDEF:
++		case IS_STRING:
++		case IS_LONG:
++		case IS_ARRAY:
++		case IS_OBJECT:
++		case IS_NULL:
++		case IS_DOUBLE:
++		case IS_RESOURCE:
++		case IS_TRUE:
++		case IS_FALSE: {
++			zend_guard *guard = NULL;
++			if ((member && Z_TYPE_P(member) != IS_NULL) && 
++				Z_OBJCE_P(object)->__set && 
++				(guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_SET)) {
++				zend_fcall_info fci = empty_fcall_info;
++				zend_fcall_info_cache fcc = empty_fcall_info_cache;
++				zval rv;
++
++				ZVAL_UNDEF(&rv);
++
++				fci.size = sizeof(zend_fcall_info);
++				fci.retval = &rv;
++				fci.object = &threaded->std;
++				zend_fcall_info_argn(&fci, 2, member, value);
++#if PHP_VERSION_ID < 70300
++				fcc.initialized = 1;
++#endif
++				fcc.function_handler = Z_OBJCE_P(object)->__set;
++				fcc.object = &threaded->std;
++
++				(*guard) |= IN_SET;
++				zend_call_function(&fci, &fcc);
++				(*guard) &= ~IN_SET;
++
++				if (Z_TYPE(rv) != IS_UNDEF)
++					zval_dtor(&rv);
++				zend_fcall_info_args_clear(&fci, 1);
++			} else {
++				pthreads_store_write(object, member, value);
++			}
++		} break;
++	
++		default: {
++			zend_throw_exception_ex(
++				spl_ce_RuntimeException, 0,
++				"pthreads detected an attempt to use unsupported data (%s) for %s::$%s", 
++				zend_get_type_by_const(Z_TYPE_P(value)), 
++				ZSTR_VAL(Z_OBJCE_P(object)->name), Z_STRVAL_P(member));
++		}
++	}
++}
++
++void pthreads_write_dimension(PTHREADS_WRITE_DIMENSION_PASSTHRU_D) { pthreads_write_property(PTHREADS_WRITE_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++void pthreads_write_property_disallow(PTHREADS_WRITE_PROPERTY_PASSTHRU_D) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++		"%s objects are not allowed to have properties",
++		ZSTR_VAL(threaded->std.ce->name));
++}
++
++void pthreads_write_dimension_disallow(PTHREADS_WRITE_DIMENSION_PASSTHRU_D) { pthreads_write_property_disallow(PTHREADS_WRITE_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++int pthreads_has_property(PTHREADS_HAS_PROPERTY_PASSTHRU_D) {
++	int isset = 0;
++	zend_guard *guard = NULL;
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	cache = NULL;
++
++	if (Z_OBJCE_P(object)->__isset && (guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_ISSET)) {
++		zend_fcall_info fci = empty_fcall_info;
++		zend_fcall_info_cache fcc = empty_fcall_info_cache;
++		zval rv;
++
++		ZVAL_UNDEF(&rv);
++
++		fci.size = sizeof(zend_fcall_info);
++		fci.retval = &rv;
++		fci.object = &threaded->std;
++		zend_fcall_info_argn(&fci, 1, member);
++#if PHP_VERSION_ID < 70300
++		fcc.initialized = 1;
++#endif
++		fcc.function_handler = Z_OBJCE_P(object)->__isset;
++		fcc.object = &threaded->std;
++
++		(*guard) |= IN_ISSET;
++		zend_call_function(&fci, &fcc);
++		(*guard) &= ~IN_ISSET;
++	
++		if (Z_TYPE(rv) != IS_UNDEF) {
++			isset = 
++				zend_is_true(&rv);
++			zval_dtor(&rv);
++		}
++		zend_fcall_info_args_clear(&fci, 1);
++	} else {
++		isset = pthreads_store_isset(object, member, has_set_exists);
++	}
++
++	return isset;
++}
++int pthreads_has_dimension(PTHREADS_HAS_DIMENSION_PASSTHRU_D) { return pthreads_has_property(PTHREADS_HAS_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++int pthreads_has_property_disallow(PTHREADS_HAS_PROPERTY_PASSTHRU_D) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++		"%s objects are not allowed to have properties",
++		ZSTR_VAL(threaded->std.ce->name));
++
++	return 0;
++}
++
++int pthreads_has_dimension_disallow(PTHREADS_HAS_DIMENSION_PASSTHRU_D) { return pthreads_has_property_disallow(PTHREADS_HAS_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++void pthreads_unset_property(PTHREADS_UNSET_PROPERTY_PASSTHRU_D) {
++	zend_guard *guard = NULL;
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	cache = NULL;
++
++	rebuild_object_properties(&threaded->std);
++
++	if (Z_OBJCE_P(object)->__unset && (guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_UNSET)) {
++		zend_fcall_info fci = empty_fcall_info;
++		zend_fcall_info_cache fcc = empty_fcall_info_cache;
++		zval rv;
++
++		ZVAL_UNDEF(&rv);
++
++		fci.size = sizeof(zend_fcall_info);
++		fci.retval = &rv;
++		fci.object = &threaded->std;
++		zend_fcall_info_argn(&fci, 1, member);
++#if PHP_VERSION_ID < 70300
++		fcc.initialized = 1;
++#endif
++		fcc.function_handler = Z_OBJCE_P(object)->__unset;
++		fcc.object = &threaded->std;
++
++		(*guard) |= IN_UNSET;
++		zend_call_function(&fci, &fcc);
++		(*guard) &= ~IN_UNSET;
++	
++		if (Z_TYPE(rv) != IS_UNDEF) {
++			zval_dtor(&rv);
++		}
++		zend_fcall_info_args_clear(&fci, 1);
++	} else {
++		if (pthreads_store_delete(object, member) == SUCCESS){
++			
++		}
++	}
++}
++void pthreads_unset_dimension(PTHREADS_UNSET_DIMENSION_PASSTHRU_D) { pthreads_unset_property(PTHREADS_UNSET_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++void pthreads_unset_property_disallow(PTHREADS_UNSET_PROPERTY_PASSTHRU_D) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++		"%s objects are not allowed to have properties",
++		ZSTR_VAL(threaded->std.ce->name));
++}
++void pthreads_unset_dimension_disallow(PTHREADS_UNSET_DIMENSION_PASSTHRU_D) { pthreads_unset_property_disallow(PTHREADS_UNSET_DIMENSION_PASSTHRU_C); }
++/* }}} */
++
++/* {{{ */
++int pthreads_cast_object(PTHREADS_CAST_PASSTHRU_D) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(from));
++	if (PTHREADS_IS_SOCKET(threaded)) {
++		if (type == IS_LONG) {
++			ZVAL_LONG(to, 
++				(int) threaded->options);
++			return SUCCESS;
++		}
++		return FAILURE;
++	}
++
++    switch (type) {
++        case IS_ARRAY: {
++            pthreads_store_tohash(from, Z_ARRVAL_P(to));
++            return SUCCESS;
++        } break;
++    }
++    
++    return zend_handlers->cast_object(PTHREADS_CAST_PASSTHRU_C);
++} /* }}} */
++
++/* {{{ */
++zend_object* pthreads_clone_object(PTHREADS_CLONE_PASSTHRU_D)
++{
++	zend_throw_exception_ex(
++			spl_ce_RuntimeException, 0, 
++			"pthreads objects cannot be cloned");
++	
++	return NULL;
++} /* }}} */
++
++/* {{{ */
++int pthreads_compare_objects(PTHREADS_COMPARE_PASSTHRU_D) {
++	pthreads_object_t *left = PTHREADS_FETCH_FROM(Z_OBJ_P(op1));
++	pthreads_object_t *right = PTHREADS_FETCH_FROM(Z_OBJ_P(op2));
++
++	/* comparing property tables is not useful or efficient for threaded objects */
++	/* in addition, it might be useful to know if two variables are infact the same physical threaded object */
++	if (left->monitor == right->monitor) {
++		return 0;
++	}
++
++	return 1;
++} /* }}} */
++#endif
+diff --git a/ext/pthreads/src/handlers.h b/ext/pthreads/src/handlers.h
+new file mode 100644
+index 00000000..97887ef8
+--- /dev/null
++++ b/ext/pthreads/src/handlers.h
+@@ -0,0 +1,121 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++
++/*
++* These handlers provide thread-safe read/write/call/count/cast for pthreads objects
++*/
++#ifndef HAVE_PTHREADS_HANDLERS_H
++#define HAVE_PTHREADS_HANDLERS_H
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++#define PTHREADS_CAST_PASSTHRU_D zval *from, zval *to, int type
++#define PTHREADS_CAST_PASSTHRU_C from, to, type
++#define PTHREADS_COUNT_PASSTHRU_D zval *object, zend_long *count
++#define PTHREADS_COUNT_PASSTHRU_C object, count
++#define PTHREADS_CLONE_PASSTHRU_D zval *object
++#define PTHREADS_CLONE_PASSTHRU_C object
++#define PTHREADS_COMPARE_PASSTHRU_D zval *op1, zval *op2
++#define PTHREADS_COMPARE_PASSTHRU_C op1, op2
++
++/* {{{  */
++#define PTHREADS_READ_DEBUG_PASSTHRU_D zval *object, int *is_temp
++#define PTHREADS_READ_DEBUG_PASSTHRU_C object, is_temp
++#define PTHREADS_READ_PROPERTIES_PASSTHRU_D zval *object
++#define PTHREADS_READ_PROPERTIES_PASSTHRU_C object
++#define PTHREADS_READ_PROPERTY_PASSTHRU_D zval *object, zval *member, int type, void **cache, zval *rv
++#define PTHREADS_READ_DIMENSION_PASSTHRU_D zval *object, zval *member, int type, zval *rv
++#define PTHREADS_READ_PROPERTY_PASSTHRU_C object, member, type, cache, rv
++#define PTHREADS_READ_DIMENSION_PASSTHRU_C object, member, type, NULL, rv
++
++#define PTHREADS_WRITE_PROPERTY_PASSTHRU_D zval *object, zval *member, zval *value, void **cache
++#define PTHREADS_WRITE_DIMENSION_PASSTHRU_D zval *object, zval *member, zval *value
++#define PTHREADS_WRITE_PROPERTY_PASSTHRU_C object, member, value, cache
++#define PTHREADS_WRITE_DIMENSION_PASSTHRU_C object, member, value, NULL
++
++#define PTHREADS_HAS_PROPERTY_PASSTHRU_D zval *object, zval *member, int has_set_exists, void **cache
++#define PTHREADS_HAS_DIMENSION_PASSTHRU_D zval *object, zval *member, int has_set_exists
++#define PTHREADS_HAS_PROPERTY_PASSTHRU_C object, member, has_set_exists, cache
++#define PTHREADS_HAS_DIMENSION_PASSTHRU_C object, member, has_set_exists, NULL
++
++#define PTHREADS_UNSET_PROPERTY_PASSTHRU_D zval *object, zval *member, void **cache
++#define PTHREADS_UNSET_DIMENSION_PASSTHRU_D zval *object, zval *member
++#define PTHREADS_UNSET_PROPERTY_PASSTHRU_C object, member, cache
++#define PTHREADS_UNSET_DIMENSION_PASSTHRU_C object, member, NULL /* }}} */
++
++/* {{{ read proeprties from storage */
++HashTable* pthreads_read_debug(PTHREADS_READ_DEBUG_PASSTHRU_D); /* }}} */
++
++/* {{{ read proeprties from storage */
++HashTable* pthreads_read_properties(PTHREADS_READ_PROPERTIES_PASSTHRU_D); /* }}} */
++
++/* {{{ read proeprties from storage */
++HashTable* pthreads_read_properties_disallow(PTHREADS_READ_PROPERTIES_PASSTHRU_D); /* }}} */
++
++/* {{{ read a property from the referenced object */
++zval * pthreads_read_property(PTHREADS_READ_PROPERTY_PASSTHRU_D); 
++zval * pthreads_read_dimension(PTHREADS_READ_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ disallow read a property from the referenced object */
++zval * pthreads_read_property_disallow(PTHREADS_READ_PROPERTY_PASSTHRU_D); 
++zval * pthreads_read_dimension_disallow(PTHREADS_READ_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ disallow write a property to the referenced object */
++void pthreads_write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_D); 
++void pthreads_write_dimension(PTHREADS_WRITE_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ disallow write a property to the referenced object */
++void pthreads_write_property_disallow(PTHREADS_WRITE_PROPERTY_PASSTHRU_D); 
++void pthreads_write_dimension_disallow(PTHREADS_WRITE_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ check if the referenced object has a specific property */
++int pthreads_has_property(PTHREADS_HAS_PROPERTY_PASSTHRU_D); 
++int pthreads_has_dimension(PTHREADS_HAS_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ disallow check if the referenced object has a specific property */
++int pthreads_has_property_disallow(PTHREADS_HAS_PROPERTY_PASSTHRU_D); 
++int pthreads_has_dimension_disallow(PTHREADS_HAS_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ unset a property in the referenced object */
++void pthreads_unset_property(PTHREADS_UNSET_PROPERTY_PASSTHRU_D); 
++void pthreads_unset_dimension(PTHREADS_UNSET_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ disallow unset a property in the referenced object */
++void pthreads_unset_property_disallow(PTHREADS_UNSET_PROPERTY_PASSTHRU_D); 
++void pthreads_unset_dimension_disallow(PTHREADS_UNSET_DIMENSION_PASSTHRU_D); /* }}} */
++
++/* {{{ count properties in storage */
++int pthreads_count_properties(PTHREADS_COUNT_PASSTHRU_D); /* }}} */
++
++/* {{{ count properties in storage */
++int pthreads_count_properties_disallow(PTHREADS_COUNT_PASSTHRU_D); /* }}} */
++
++/* {{{ cast an object to a normal array helper */
++int pthreads_cast_object(PTHREADS_CAST_PASSTHRU_D); /* }}} */
++
++/* {{{ cast an object to a normal array helper */
++int pthreads_cast_object_disallow(PTHREADS_CAST_PASSTHRU_D); /* }}} */
++
++/* {{{ clone object handler */
++zend_object* pthreads_clone_object(PTHREADS_CLONE_PASSTHRU_D); /* }}} */
++
++/* {{{ */
++int pthreads_compare_objects(PTHREADS_COMPARE_PASSTHRU_D); /* }}} */
++#endif
+diff --git a/ext/pthreads/src/monitor.c b/ext/pthreads/src/monitor.c
+new file mode 100644
+index 00000000..771fae56
+--- /dev/null
++++ b/ext/pthreads/src/monitor.c
+@@ -0,0 +1,132 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2015                                       |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_MONITOR
++#define HAVE_PTHREADS_MONITOR
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++struct _pthreads_monitor_t {
++	pthreads_monitor_state_t state;	
++	pthread_mutex_t			 mutex;	
++	pthread_cond_t			 cond;
++};
++
++pthreads_monitor_t* pthreads_monitor_alloc() {
++	pthread_mutexattr_t at;
++	pthreads_monitor_t *m = 
++		(pthreads_monitor_t*) calloc(1, sizeof(pthreads_monitor_t));
++
++	pthread_mutexattr_init(&at);
++#if defined(PTHREAD_MUTEX_RECURSIVE) || defined(__FreeBSD__)
++	pthread_mutexattr_settype(&at, PTHREAD_MUTEX_RECURSIVE);
++#else
++	pthread_mutexattr_settype(&at, PTHREAD_MUTEX_RECURSIVE_NP);
++#endif
++	int ret = pthread_mutex_init(&m->mutex, &at);
++	pthread_mutexattr_destroy(&at);
++	if (ret != 0) {
++		free(m);
++		return NULL;
++	}
++
++	if (pthread_cond_init(&m->cond, NULL) != 0) {
++		pthread_mutex_destroy(&m->mutex);
++		free(m);
++		return NULL;
++	}
++
++	return m;
++}
++
++zend_bool pthreads_monitor_lock(pthreads_monitor_t *m) {
++	return (pthread_mutex_lock(&m->mutex) == 0);
++}
++
++zend_bool pthreads_monitor_unlock(pthreads_monitor_t *m) {
++	return (pthread_mutex_unlock(&m->mutex) == 0);
++}
++
++pthreads_monitor_state_t pthreads_monitor_check(pthreads_monitor_t *m, pthreads_monitor_state_t state) {
++	return (m->state & state);
++}
++
++int pthreads_monitor_wait(pthreads_monitor_t *m, long timeout) {
++	struct timeval time;
++	struct timespec spec;
++	
++	if (timeout == 0) {
++		return pthread_cond_wait(&m->cond, &m->mutex);
++	}
++	
++	if (gettimeofday(&time, NULL) != 0) {
++		return -1;
++	}
++
++	time.tv_sec += (timeout / 1000000L);
++	time.tv_sec += (time.tv_usec + (timeout % 1000000L)) / 1000000L;
++	time.tv_usec = (time.tv_usec + (timeout % 1000000L)) % 1000000L;
++
++	spec.tv_sec = time.tv_sec;
++	spec.tv_nsec = time.tv_usec * 1000;
++
++	return pthread_cond_timedwait(&m->cond, &m->mutex, &spec);
++}
++
++int pthreads_monitor_notify(pthreads_monitor_t *m) {
++	return pthread_cond_broadcast(&m->cond);
++}
++
++int pthreads_monitor_notify_one(pthreads_monitor_t *m) {
++	return pthread_cond_signal(&m->cond);
++}
++
++void pthreads_monitor_wait_until(pthreads_monitor_t *m, pthreads_monitor_state_t state) {
++	if (pthreads_monitor_lock(m)) {
++		while (!pthreads_monitor_check(m, state)) {
++			if (pthreads_monitor_wait(m, 0) != 0) {
++				break;
++			}
++		}
++		pthreads_monitor_unlock(m);
++	}
++}
++
++void pthreads_monitor_add(pthreads_monitor_t *m, pthreads_monitor_state_t state) {
++	if (pthreads_monitor_lock(m)) {
++		m->state |= state;
++		pthreads_monitor_notify(m);
++		pthreads_monitor_unlock(m);
++	}
++}
++
++void pthreads_monitor_remove(pthreads_monitor_t *m, pthreads_monitor_state_t state) {
++	if (pthreads_monitor_lock(m)) {
++		m->state &= ~state;
++		pthreads_monitor_notify(m);
++		pthreads_monitor_unlock(m);
++	}
++}
++
++void pthreads_monitor_free(pthreads_monitor_t *m) {
++	pthread_mutex_destroy(&m->mutex);
++	pthread_cond_destroy(&m->cond);
++	free(m);
++}
++#endif
+diff --git a/ext/pthreads/src/monitor.h b/ext/pthreads/src/monitor.h
+new file mode 100644
+index 00000000..2936bb26
+--- /dev/null
++++ b/ext/pthreads/src/monitor.h
+@@ -0,0 +1,43 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2015                                       |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_MONITOR_H
++#define HAVE_PTHREADS_MONITOR_H
++
++typedef volatile unsigned long pthreads_monitor_state_t;
++
++typedef struct _pthreads_monitor_t pthreads_monitor_t;
++
++#define PTHREADS_MONITOR_NOTHING	(0)
++#define PTHREADS_MONITOR_STARTED	(1<<0)
++#define PTHREADS_MONITOR_RUNNING	(1<<1)
++#define PTHREADS_MONITOR_JOINED		(1<<2)
++#define PTHREADS_MONITOR_ERROR		(1<<3)
++#define PTHREADS_MONITOR_READY		(1<<4)
++
++pthreads_monitor_t* pthreads_monitor_alloc();
++zend_bool pthreads_monitor_lock(pthreads_monitor_t *m);
++zend_bool pthreads_monitor_unlock(pthreads_monitor_t *m);
++pthreads_monitor_state_t pthreads_monitor_check(pthreads_monitor_t *m, pthreads_monitor_state_t state);
++int pthreads_monitor_wait(pthreads_monitor_t *m, long timeout);
++int pthreads_monitor_notify(pthreads_monitor_t *m);
++int pthreads_monitor_notify_one(pthreads_monitor_t *m);
++void pthreads_monitor_wait_until(pthreads_monitor_t *m, pthreads_monitor_state_t state);
++void pthreads_monitor_add(pthreads_monitor_t *m, pthreads_monitor_state_t state);
++void pthreads_monitor_remove(pthreads_monitor_t *m, pthreads_monitor_state_t state);
++void pthreads_monitor_free(pthreads_monitor_t *m);
++#endif
+diff --git a/ext/pthreads/src/object.c b/ext/pthreads/src/object.c
+new file mode 100644
+index 00000000..66f73e1e
+--- /dev/null
++++ b/ext/pthreads/src/object.c
+@@ -0,0 +1,536 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_OBJECT
++#define HAVE_PTHREADS_OBJECT
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++#ifndef HAVE_PTHREADS_OBJECT_H
++#	include <src/object.h>
++#endif
++
++#ifndef HAVE_PTHREADS_GLOBALS_H
++#	include <src/globals.h>
++#endif
++
++#ifndef HAVE_PTHREADS_PREPARE_H
++#	include <src/prepare.h>
++#endif
++
++/* {{{ */
++extern zend_module_entry pthreads_module_entry; /* }}} */
++
++/* {{{ */
++static void pthreads_base_ctor(pthreads_object_t* base, zend_class_entry *entry); /* }}} */
++
++/* {{{ */
++static void * pthreads_routine(pthreads_routine_arg_t *arg); /* }}} */
++
++static inline void pthreads_object_iterator_dtor(pthreads_iterator_t* iterator) {
++	if (Z_TYPE(iterator->zit.data) != IS_UNDEF)
++		zval_ptr_dtor(&iterator->zit.data);
++	zval_ptr_dtor(&iterator->object);
++}
++
++static inline int pthreads_object_iterator_validate(pthreads_iterator_t* iterator) {
++	return (iterator->position != HT_INVALID_IDX) ? SUCCESS : FAILURE;
++}
++
++static inline zval* pthreads_object_iterator_current_data(pthreads_iterator_t* iterator) {
++	if (Z_TYPE(iterator->zit.data) != IS_UNDEF) {
++		zval_ptr_dtor(&iterator->zit.data);
++	}
++
++	pthreads_store_data(&iterator->object, &iterator->zit.data, &iterator->position);
++
++	if (Z_ISUNDEF(iterator->zit.data)) {
++		return &EG(uninitialized_zval);
++	}
++
++    return &iterator->zit.data;
++}
++
++static inline void pthreads_object_iterator_current_key(pthreads_iterator_t* iterator, zval* result) {
++    pthreads_store_key(&iterator->object, result, &iterator->position);
++}
++
++static inline void pthreads_object_iterator_move_forward(pthreads_iterator_t* iterator) {
++    pthreads_store_forward(&iterator->object, &iterator->position);
++}
++
++static inline void pthreads_object_iterator_rewind(pthreads_iterator_t* iterator) {
++    pthreads_store_reset(&iterator->object, &iterator->position);
++}
++
++static zend_object_iterator_funcs pthreads_object_iterator_funcs = {
++    (void (*) (zend_object_iterator*)) 				pthreads_object_iterator_dtor,
++    (int (*)(zend_object_iterator *)) 				pthreads_object_iterator_validate,
++    (zval* (*)(zend_object_iterator *)) 			pthreads_object_iterator_current_data,
++    (void (*)(zend_object_iterator *, zval *)) 		pthreads_object_iterator_current_key,
++    (void (*)(zend_object_iterator *))				pthreads_object_iterator_move_forward,
++    (void (*)(zend_object_iterator *)) 				pthreads_object_iterator_rewind
++};
++
++zend_object_iterator* pthreads_object_iterator_create(zend_class_entry *ce, zval *object, int by_ref) {
++    pthreads_iterator_t *iterator;
++
++	if (by_ref) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0, 
++			"iteration by reference is not allowed on %s objects", ZSTR_VAL(ce->name));
++		return NULL;
++	}
++	
++	iterator = (pthreads_iterator_t*) 
++		ecalloc(1, sizeof(pthreads_iterator_t));
++	
++    zend_iterator_init((zend_object_iterator*)iterator);
++
++	ZVAL_COPY(&iterator->object, object);
++	ZVAL_UNDEF(&iterator->zit.data);
++
++	pthreads_store_reset(&iterator->object, &iterator->position);
++
++    iterator->zit.funcs = &pthreads_object_iterator_funcs;
++
++    return (zend_object_iterator*) iterator;
++}
++
++/* {{{ */
++static void pthreads_routine_init(pthreads_routine_arg_t *r, pthreads_object_t *thread) {
++	r->thread = thread;
++	r->ready  = pthreads_monitor_alloc();
++	pthreads_monitor_add(
++		r->thread->monitor, PTHREADS_MONITOR_STARTED);
++	pthreads_prepare_parent(thread);
++}
++
++static void pthreads_routine_wait(pthreads_routine_arg_t *r) {
++	pthreads_monitor_wait_until(
++		r->ready, PTHREADS_MONITOR_READY);
++	pthreads_monitor_free(r->ready);
++}
++
++static void pthreads_routine_free(pthreads_routine_arg_t *r) {
++	pthreads_monitor_remove(
++		r->thread->monitor, PTHREADS_MONITOR_STARTED);
++	pthreads_monitor_free(r->ready);
++} /* }}} */
++
++/* {{{ */
++zend_object* pthreads_thread_ctor(zend_class_entry *entry) {
++	pthreads_object_t* thread = pthreads_globals_object_alloc(
++		sizeof(pthreads_object_t) + zend_object_properties_size(entry));
++
++	thread->scope = PTHREADS_SCOPE_THREAD;
++	pthreads_base_ctor(thread, entry);
++	thread->std.handlers = &pthreads_handlers;
++
++	return &thread->std;
++} /* }}} */
++
++/* {{{ */
++zend_object* pthreads_worker_ctor(zend_class_entry *entry) {
++	pthreads_object_t* worker = pthreads_globals_object_alloc(
++		sizeof(pthreads_object_t) + zend_object_properties_size(entry));
++
++	worker->scope = PTHREADS_SCOPE_WORKER;
++	pthreads_base_ctor(worker, entry);
++	worker->std.handlers = &pthreads_handlers;
++
++	return &worker->std;
++} /* }}} */
++
++/* {{{ */
++zend_object* pthreads_threaded_ctor(zend_class_entry *entry) {
++	pthreads_object_t* threaded = pthreads_globals_object_alloc(
++		sizeof(pthreads_object_t) + zend_object_properties_size(entry));
++
++	threaded->scope = PTHREADS_SCOPE_THREADED;
++	pthreads_base_ctor(threaded, entry);
++	threaded->std.handlers = &pthreads_handlers;
++
++	return &threaded->std;
++} /* }}} */
++
++/* {{{ */
++zend_object* pthreads_socket_ctor(zend_class_entry *entry) {
++	pthreads_object_t* threaded = pthreads_globals_object_alloc(
++		sizeof(pthreads_object_t) + zend_object_properties_size(entry));
++
++	threaded->scope = PTHREADS_SCOPE_SOCKET;
++	pthreads_base_ctor(threaded, entry);
++	threaded->std.handlers = &pthreads_socket_handlers;
++
++	return &threaded->std;
++} /* }}} */
++
++/* {{{ */
++int pthreads_threaded_serialize(zval *object, unsigned char **buffer, size_t *buflen, zend_serialize_data *data) {
++	pthreads_object_t *address = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++#ifdef _WIN64
++	(*buflen) = snprintf(NULL, 0, ":%I64u:", (unsigned __int64) address);
++#else
++	(*buflen) = snprintf(NULL, 0, ":%lu:", (long unsigned int) address);
++#endif
++	(*buffer) = emalloc((*buflen) + 1);
++	sprintf(
++#ifdef _WIN64
++		(char*) (*buffer), ":%I64u:", (unsigned __int64) address);
++#else
++		(char*) (*buffer), ":%lu:", (long unsigned int) address);
++#endif
++	(*buffer)[(*buflen)] = 0;
++
++	return SUCCESS;
++} /* }}} */
++
++/* {{{ */
++int pthreads_threaded_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buffer, size_t buflen, zend_unserialize_data *data) {
++	pthreads_object_t *address = NULL;	
++
++#ifdef _WIN64
++	if (!sscanf((const char*) buffer, ":%I64u:", (unsigned __int64*)&address)) {
++#else
++	if (!sscanf((const char*) buffer, ":%lu:", (long unsigned int*)&address)) {
++#endif
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++			"pthreads detected an attempt to connect to a corrupted object");
++		return FAILURE;
++	}
++
++	if (!address) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0, 
++			"pthreads detected an attempt to connect to an invalid object");
++		return FAILURE;	
++	}
++	
++	if (!pthreads_globals_object_connect((zend_ulong) address, ce, object)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0, 
++			"pthreads detected an attempt to connect to an object which has already been destroyed");
++		return FAILURE;
++	}
++
++	return SUCCESS;
++} /* }}} */
++
++/* {{{ */
++void pthreads_current_thread(zval *return_value) {
++	if (Z_TYPE(PTHREADS_ZG(this)) != IS_UNDEF) {
++		ZVAL_COPY(return_value, &PTHREADS_ZG(this));
++	}
++} /* }}} */
++
++/* {{{ */
++int pthreads_connect(pthreads_object_t* source, pthreads_object_t* destination) {
++	if (source && destination) {
++		pthreads_ident_t destCreator = destination->creator;
++
++		if (PTHREADS_IS_NOT_CONNECTION(destination)) {
++			if (!PTHREADS_IS_SOCKET(destination)) {
++				pthreads_store_free(destination->store.props);
++				if (PTHREADS_IS_WORKER(destination)) {
++					pthreads_stack_free(destination->stack);
++				}
++				free(destination->running);
++			} else {
++				pthreads_socket_free(destination->store.sock, 0);
++			}
++
++			pthreads_monitor_free(destination->monitor);
++		}
++
++		memcpy(destination, source, sizeof(pthreads_object_t) - sizeof(zend_object));
++		
++		destination->creator = destCreator;
++		destination->scope |= PTHREADS_SCOPE_CONNECTION;
++
++		if (destination->std.properties)
++			zend_hash_clean(destination->std.properties);
++		
++		return SUCCESS;
++	} else return FAILURE;
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_base_init(pthreads_object_t* base) {	
++	zend_property_info *info;
++	zval tmp, key;
++
++	ZVAL_OBJ(&tmp, &base->std);	
++
++	ZEND_HASH_FOREACH_PTR(&base->std.ce->properties_info, info) {
++		zend_ulong offset;
++		const char *clazz = NULL, 
++			    *prop = NULL;
++		size_t plen = 0;
++
++		if (info->flags & ZEND_ACC_STATIC) {
++			continue;
++		}
++
++		offset = OBJ_PROP_TO_NUM(info->offset);
++
++		zend_unmangle_property_name_ex(
++			info->name, &clazz, &prop, &plen);
++
++		ZVAL_STR(&key, zend_string_init(prop, plen, 0));
++		pthreads_store_write(
++			&tmp, &key,
++			&base->std.ce->default_properties_table[offset]);
++		zval_ptr_dtor(&key);
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++static void pthreads_base_ctor(pthreads_object_t* base, zend_class_entry *entry) {
++	zend_object_std_init(&base->std, entry);
++	object_properties_init(&base->std, entry);
++
++	base->creator.ls = TSRMLS_CACHE;
++	base->creator.id = pthreads_self();
++	base->options = PTHREADS_INHERIT_ALL;
++
++	if (PTHREADS_IS_NOT_CONNECTION(base)) {
++		base->monitor = pthreads_monitor_alloc();
++		if (!PTHREADS_IS_SOCKET(base)) {
++			base->store.props   = pthreads_store_alloc();
++			base->running = malloc(sizeof(pthreads_object_t**));
++
++			if (PTHREADS_IS_WORKER(base)) {
++				base->stack = pthreads_stack_alloc(base->monitor);
++			}
++			pthreads_base_init(base);
++		} else {
++			base->store.sock = pthreads_socket_alloc();
++		}
++	}
++} /* }}} */
++
++/* {{{ */
++void pthreads_base_free(zend_object *object) {
++	pthreads_object_t* base = PTHREADS_FETCH_FROM(object);
++
++	if (PTHREADS_IS_NOT_CONNECTION(base)) {
++		if (!PTHREADS_IS_SOCKET(base)) {
++			if ((PTHREADS_IS_THREAD(base)||PTHREADS_IS_WORKER(base)) &&
++				pthreads_monitor_check(base->monitor, PTHREADS_MONITOR_STARTED) &&
++				!pthreads_monitor_check(base->monitor, PTHREADS_MONITOR_JOINED)) {
++				pthreads_join(base);
++			}
++
++			if (pthreads_monitor_lock(base->monitor)) {
++				pthreads_store_free(base->store.props);
++				if (PTHREADS_IS_WORKER(base)) {
++					pthreads_stack_free(base->stack);	
++				}
++				pthreads_monitor_unlock(base->monitor);
++			}
++
++			if (base->running) {
++				free(base->running);
++			}
++		} else {
++			pthreads_socket_free(base->store.sock, 1);
++		}
++
++		pthreads_monitor_free(base->monitor);
++	}
++
++	zend_object_std_dtor(object);
++
++	pthreads_globals_object_delete(base);
++} /* }}} */
++
++/* {{{ */
++zend_object* pthreads_base_clone(zval *object) {
++	zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++		"%s objects cannot be cloned", ZSTR_VAL(Z_OBJCE_P(object)->name));
++
++	/* assume this is okay ? */
++	return Z_OBJ_P(object);
++} /* }}} */
++
++/* {{{ */
++HashTable* pthreads_base_gc(zval *object, zval **table, int *n) {
++	*table = NULL;
++	*n = 0;
++	return Z_OBJ_P(object)->properties;
++} /* }}} */
++
++/* {{{ */
++zend_bool pthreads_start(pthreads_object_t* thread) {
++	pthreads_routine_arg_t routine;
++
++	if (!PTHREADS_IN_CREATOR(thread) || PTHREADS_IS_CONNECTION(thread)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 
++			0, "only the creator of this %s may start it",
++			thread->std.ce->name->val);
++		return 0;
++	}
++
++	if (pthreads_monitor_check(thread->monitor, PTHREADS_MONITOR_STARTED)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++			"the creator of %s already started it", thread->std.ce->name->val);
++		return 0;
++	}
++
++	pthreads_routine_init(&routine, thread);
++
++	switch (pthread_create(&thread->thread, NULL, (void* (*) (void*)) pthreads_routine, (void*)&routine)) {
++		case SUCCESS:
++			pthreads_routine_wait(&routine);
++			return 1;
++
++		case EAGAIN:
++			zend_throw_exception_ex(spl_ce_RuntimeException,
++				0, "cannot start %s, out of resources", thread->std.ce->name->val);
++		break;
++
++		default:
++			zend_throw_exception_ex(spl_ce_RuntimeException,
++				0, "cannot start %s, unknown error", thread->std.ce->name->val);
++	}
++	
++	pthreads_routine_free(&routine);
++
++	return 0;
++} /* }}} */
++
++/* {{{ */
++zend_bool pthreads_join(pthreads_object_t* thread) {
++
++	if (!PTHREADS_IN_CREATOR(thread) || PTHREADS_IS_CONNECTION(thread)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 
++			0, "only the creator of this %s may join with it",
++			thread->std.ce->name->val);
++		return 0;
++	}
++
++	if (pthreads_monitor_check(thread->monitor, PTHREADS_MONITOR_JOINED)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++			"the creator of %s already joined with it",
++			thread->std.ce->name->val);
++		return 0;
++	}
++
++	if (!pthreads_monitor_check(thread->monitor, PTHREADS_MONITOR_STARTED)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++			"%s has not been started",
++			thread->std.ce->name->val);
++		return 0;
++	}
++
++	pthreads_monitor_add(thread->monitor, PTHREADS_MONITOR_JOINED);
++
++	return (pthread_join(thread->thread, NULL) == SUCCESS);
++} /* }}} */
++
++/* {{{ */
++static inline zend_bool pthreads_routine_run_function(pthreads_object_t* object, pthreads_object_t* connection, zval *work) {
++	zend_function *run;
++	pthreads_call_t call = PTHREADS_CALL_EMPTY;
++	zval zresult;	
++
++	if (pthreads_connect(object, connection) != SUCCESS) {
++		return 0;
++	}
++
++	if (pthreads_monitor_check(object->monitor, PTHREADS_MONITOR_ERROR)) {
++		return 0;
++	}
++
++	ZVAL_UNDEF(&zresult);
++
++	pthreads_monitor_add(object->monitor, PTHREADS_MONITOR_RUNNING);
++
++	if (work)
++		pthreads_store_write(work, &PTHREADS_G(strings).worker, &PTHREADS_ZG(this));
++
++	zend_try {
++		if ((run = zend_hash_find_ptr(&connection->std.ce->function_table, PTHREADS_G(strings).run))) {							
++			if (run->type == ZEND_USER_FUNCTION) {
++				call.fci.size = sizeof(zend_fcall_info);
++			    call.fci.retval = &zresult;
++				call.fci.object = &connection->std;
++				call.fci.no_separation = 1;
++#if PHP_VERSION_ID < 70300
++				call.fcc.initialized = 1;
++#endif
++				call.fcc.object = &connection->std;
++				call.fcc.calling_scope = connection->std.ce;
++				call.fcc.called_scope = connection->std.ce;
++				call.fcc.function_handler = run;
++
++				zend_call_function(&call.fci, &call.fcc);
++			}
++		}
++	} zend_catch {
++	    pthreads_monitor_add(object->monitor, PTHREADS_MONITOR_ERROR);
++	} zend_end_try();
++
++	if (Z_TYPE(zresult) != IS_UNDEF) {
++		zval_ptr_dtor(&zresult);
++	}
++
++	pthreads_monitor_remove(object->monitor, PTHREADS_MONITOR_RUNNING);
++
++	return 1;
++} /* }}} */
++
++/* {{{ */
++static void * pthreads_routine(pthreads_routine_arg_t *routine) {
++	pthreads_object_t* thread = routine->thread;
++	pthreads_monitor_t* ready = routine->ready;
++	
++	if (pthreads_prepared_startup(thread, ready) == SUCCESS) {
++		
++		zend_first_try {
++			ZVAL_UNDEF(&PTHREADS_ZG(this));
++			object_init_ex(&PTHREADS_ZG(this), pthreads_prepared_entry(thread, thread->std.ce));
++			pthreads_routine_run_function(thread, PTHREADS_FETCH_FROM(Z_OBJ_P(&PTHREADS_ZG(this))), NULL);
++
++			if (PTHREADS_IS_WORKER(thread)) {
++				zval stacked;
++
++				while (pthreads_stack_next(thread->stack, &stacked, thread->running) != PTHREADS_MONITOR_JOINED) {
++					zval that;
++					pthreads_object_t* work = PTHREADS_FETCH_FROM(Z_OBJ(stacked));
++					object_init_ex(&that, pthreads_prepared_entry(thread, work->std.ce));
++					pthreads_routine_run_function(work, PTHREADS_FETCH_FROM(Z_OBJ(that)), &that);
++					zval_ptr_dtor(&that);
++				}
++			}
++
++			zval_ptr_dtor(&PTHREADS_ZG(this));
++			ZVAL_UNDEF(&PTHREADS_ZG(this));
++		} zend_end_try();	
++	}
++
++	pthreads_prepared_shutdown();
++
++	pthread_exit(NULL);
++
++#ifdef _WIN32
++	return NULL;
++#endif
++} /* }}} */
++
++#endif
++
+diff --git a/ext/pthreads/src/object.h b/ext/pthreads/src/object.h
+new file mode 100644
+index 00000000..b89da587
+--- /dev/null
++++ b/ext/pthreads/src/object.h
+@@ -0,0 +1,60 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_OBJECT_H
++#define HAVE_PTHREADS_OBJECT_H
++
++#ifdef HAVE_CONFIG_H
++#	include <config.h>
++#endif
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++/* {{{ */
++zend_object* pthreads_threaded_ctor(zend_class_entry *entry);
++zend_object* pthreads_worker_ctor(zend_class_entry *entry);
++zend_object* pthreads_thread_ctor(zend_class_entry *entry);
++zend_object* pthreads_socket_ctor(zend_class_entry *entry);
++void         pthreads_base_free(zend_object *object);
++zend_object* pthreads_base_clone(zval *object);
++HashTable*   pthreads_base_gc(zval *object, zval **table, int *n); /* }}} */
++
++/* {{{ */
++int pthreads_threaded_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buffer, size_t buflen, zend_unserialize_data *data);
++int pthreads_threaded_serialize(zval *object, unsigned char **buffer, size_t *buflen, zend_serialize_data *data); /* }}} */
++
++/* {{{ */
++void pthreads_current_thread(zval *return_value); /* }}} */
++
++/* {{{ */
++zend_bool pthreads_start(pthreads_object_t* thread);
++zend_bool pthreads_join(pthreads_object_t* thread); /* }}} */
++
++/* {{{ */
++int pthreads_connect(pthreads_object_t* source, pthreads_object_t* destination); /* }}} */
++
++/* {{{ */
++zend_object_iterator* pthreads_object_iterator_create(zend_class_entry *ce, zval *object, int by_ref); /* }}} */
++
++/* {{{ */
++#ifndef HAVE_PTHREADS_HANDLERS_H
++#	include <src/handlers.h>
++#endif /* }}} */
++
++#endif /* HAVE_PTHREADS_OBJECT_H */
+diff --git a/ext/pthreads/src/prepare.c b/ext/pthreads/src/prepare.c
+new file mode 100644
+index 00000000..896e9374
+--- /dev/null
++++ b/ext/pthreads/src/prepare.c
+@@ -0,0 +1,894 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_PREPARE
++#define HAVE_PTHREADS_PREPARE
++
++#ifndef HAVE_PTHREADS_PREPARE_H
++#	include <src/prepare.h>
++#endif
++
++#ifndef HAVE_PTHREADS_OBJECT_H
++#	include <src/object.h>
++#endif
++
++#ifndef HAVE_PTHREADS_RESOURCES_H
++#	include <src/resources.h>
++#endif
++
++#ifndef HAVE_PTHREADS_GLOBALS_H
++#	include <src/globals.h>
++#endif
++
++#ifndef HAVE_PTHREADS_COPY_H
++#   include <src/copy.h>
++#endif
++
++#define PTHREADS_PREPARATION_BEGIN_CRITICAL() pthreads_globals_lock();
++#define PTHREADS_PREPARATION_END_CRITICAL()   pthreads_globals_unlock()
++
++/* {{{ */
++static zend_trait_alias * pthreads_preparation_copy_trait_alias(pthreads_object_t* thread, zend_trait_alias *alias); 
++static zend_trait_precedence * pthreads_preparation_copy_trait_precedence(pthreads_object_t* thread, zend_trait_precedence *precedence);
++#if PHP_VERSION_ID >= 70300
++static void pthreads_preparation_copy_trait_method_reference(pthreads_object_t* thread, zend_trait_method_reference *reference, zend_trait_method_reference *copy);
++#else
++static zend_trait_method_reference * pthreads_preparation_copy_trait_method_reference(pthreads_object_t* thread, zend_trait_method_reference *reference);
++#endif
++static void pthreads_prepared_resource_dtor(zval *zv); /* }}} */
++
++/* {{{ */
++static void prepare_class_constants(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared) {
++
++	zend_string *key;
++	zval *value;
++
++	ZEND_HASH_FOREACH_STR_KEY_VAL(&candidate->constants_table, key, value) {
++		zend_string *name;
++		zval separated;
++
++		if (zend_hash_exists(&prepared->constants_table, key)) {
++			continue;
++		}
++
++		switch (Z_TYPE_P(value)) {
++			case IS_PTR: {
++				zend_class_constant *zc = Z_PTR_P(value), rc;
++
++				memcpy(&rc, zc, sizeof(zend_class_constant));
++
++				if (pthreads_store_separate(&zc->value, &rc.value, 1) == SUCCESS) {
++					if (zc->doc_comment != NULL) {
++						rc.doc_comment = zend_string_new(zc->doc_comment);
++					}
++					rc.ce = pthreads_prepared_entry(thread, zc->ce);
++
++					name = zend_string_new(key);
++					zend_hash_add_mem(&prepared->constants_table, name, &rc, sizeof(zend_class_constant));
++					zend_string_release(name);
++				}
++				continue;
++			}
++
++			case IS_STRING:
++			case IS_ARRAY: {
++				if (pthreads_store_separate(value, &separated, 1) != SUCCESS) {
++					continue;
++				}
++			} break;
++
++			default: ZVAL_COPY(&separated, value);
++		}
++
++		name = zend_string_new(key);
++		zend_hash_update(&prepared->constants_table, name, &separated);
++		zend_string_release(name);
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++static void prepare_class_statics(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared) {
++	if (candidate->default_static_members_count) {
++		int i;
++
++		if(prepared->default_static_members_table != NULL) {
++			efree(prepared->default_static_members_table);
++		}
++		prepared->default_static_members_table = (zval*) ecalloc(
++			sizeof(zval), candidate->default_static_members_count);
++		prepared->default_static_members_count = candidate->default_static_members_count;
++		memcpy(prepared->default_static_members_table,
++		       candidate->default_static_members_table,
++			sizeof(zval) * candidate->default_static_members_count);
++
++		for (i=0; i<prepared->default_static_members_count; i++) {
++			pthreads_store_separate(
++				&candidate->default_static_members_table[i],
++				&prepared->default_static_members_table[i], 0);
++		}	
++		prepared->static_members_table = prepared->default_static_members_table;
++	} else prepared->default_static_members_count = 0;
++} /* }}} */
++
++/* {{{ */
++static void prepare_class_function_table(zend_class_entry *candidate, zend_class_entry *prepared) {
++
++	zend_string *key;
++	zend_function *value;
++
++	ZEND_HASH_FOREACH_STR_KEY_PTR(&candidate->function_table, key, value) {
++		if (!zend_hash_exists(&prepared->function_table, key)) {
++			zend_string *name = zend_string_new(key);
++
++			value = pthreads_copy_function(value);
++			zend_hash_add_ptr(&prepared->function_table, name, value);
++			zend_string_release(name);
++		}
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++static void prepare_class_property_table(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared) {
++
++	zend_property_info *info;
++	zend_string *name;
++	ZEND_HASH_FOREACH_STR_KEY_PTR(&candidate->properties_info, name, info) {
++		zend_property_info dup = *info;
++		if (info->doc_comment) {
++			if (thread->options & PTHREADS_INHERIT_COMMENTS) {
++				dup.doc_comment = zend_string_new(info->doc_comment);
++			} else dup.doc_comment = NULL;
++		}
++
++		if (info->ce) {
++			if (info->ce == candidate) {
++				dup.ce = prepared;
++			} else dup.ce = pthreads_prepared_entry(thread, info->ce);
++		}
++
++		if (!zend_hash_str_add_mem(&prepared->properties_info, name->val, name->len, &dup, sizeof(zend_property_info))) {
++			if (dup.doc_comment)
++				zend_string_release(dup.doc_comment);
++		}
++	} ZEND_HASH_FOREACH_END();
++
++	if (candidate->default_properties_count) {
++		int i;
++
++		if(prepared->default_properties_table != NULL) {
++			efree(prepared->default_properties_table);
++		}
++		prepared->default_properties_table = emalloc(
++			sizeof(zval) * candidate->default_properties_count);
++
++		memcpy(
++			prepared->default_properties_table,
++			candidate->default_properties_table,
++			sizeof(zval) * candidate->default_properties_count);
++
++		for (i=0; i<candidate->default_properties_count; i++) {
++			if (Z_REFCOUNTED(prepared->default_properties_table[i])) {
++				pthreads_store_separate(
++					&candidate->default_properties_table[i],
++					&prepared->default_properties_table[i], 1);
++			}
++		}
++		prepared->default_properties_count = candidate->default_properties_count;
++	} else prepared->default_properties_count = 0;
++} /* }}} */
++
++/* {{{ */
++static void prepare_class_handlers(zend_class_entry *candidate, zend_class_entry *prepared) {
++	prepared->create_object = candidate->create_object;
++	prepared->serialize = candidate->serialize;
++	prepared->unserialize = candidate->unserialize;
++	prepared->get_iterator = candidate->get_iterator;
++#if PHP_VERSION_ID < 70300
++	prepared->iterator_funcs = candidate->iterator_funcs;
++#endif
++	prepared->interface_gets_implemented = candidate->interface_gets_implemented;
++	prepared->get_static_method = candidate->get_static_method;
++} /* }}} */
++
++static void prepare_class_interceptors(zend_class_entry *candidate, zend_class_entry *prepared) {
++	zend_function *func;
++
++	if (!prepared->constructor && zend_hash_num_elements(&prepared->function_table)) {
++		if ((func = zend_hash_str_find_ptr(&prepared->function_table, "__construct", sizeof("__construct")-1))) {
++			prepared->constructor = func;
++		} else {
++			if ((func = zend_hash_find_ptr(&prepared->function_table, prepared->name))) {
++				prepared->constructor = func;
++			}
++		}
++	}
++
++#define FIND_AND_SET(f, n) do {\
++    if (!prepared->f && zend_hash_num_elements(&prepared->function_table)) { \
++        if ((func = zend_hash_str_find_ptr(&prepared->function_table, n, sizeof(n)-1))) { \
++            prepared->f = func; \
++        } \
++    } \
++} \
++while(0)
++        
++	FIND_AND_SET(clone, "__clone");
++	FIND_AND_SET(__get, "__get");
++	FIND_AND_SET(__set, "__set");
++	FIND_AND_SET(__unset, "__unset");
++	FIND_AND_SET(__isset, "__isset");
++	FIND_AND_SET(__call, "__call");
++	FIND_AND_SET(__callstatic, "__callstatic");
++	FIND_AND_SET(serialize_func, "serialize");
++	FIND_AND_SET(unserialize_func, "unserialize");
++	FIND_AND_SET(__tostring, "__tostring");
++	FIND_AND_SET(destructor, "__destruct");
++#undef FIND_AND_SET
++
++#if PHP_VERSION_ID >= 70300
++#define SET_ITERATOR_FUNC(f) do { \
++	if (candidate->iterator_funcs_ptr->f) { \
++		prepared->iterator_funcs_ptr->f = zend_hash_index_find_ptr( \
++			&PTHREADS_ZG(resolve), (zend_ulong) candidate->iterator_funcs_ptr->f); \
++	} \
++} while (0)
++#else
++#define SET_ITERATOR_FUNC(f) do { \
++	if (candidate->iterator_funcs.f) { \
++		prepared->iterator_funcs.f = zend_hash_index_find_ptr( \
++			&PTHREADS_ZG(resolve), (zend_ulong) candidate->iterator_funcs.f); \
++	} \
++} while (0)
++#endif
++
++#if PHP_VERSION_ID >= 70300
++	if (candidate->iterator_funcs_ptr) {
++		prepared->iterator_funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
++		memset(prepared->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
++#else
++		memcpy(&prepared->iterator_funcs, &candidate->iterator_funcs, sizeof(zend_class_iterator_funcs));
++#endif
++		SET_ITERATOR_FUNC(zf_new_iterator);
++		SET_ITERATOR_FUNC(zf_valid);
++		SET_ITERATOR_FUNC(zf_current);
++		SET_ITERATOR_FUNC(zf_key);
++		SET_ITERATOR_FUNC(zf_next);
++		SET_ITERATOR_FUNC(zf_rewind);
++#if PHP_VERSION_ID >= 70300
++	} else prepared->iterator_funcs_ptr = NULL;
++#endif
++
++#undef SET_ITERATOR_FUNC
++} /* }}} */
++
++/* {{{ */
++static void prepare_class_traits(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared) {
++
++	if (candidate->num_traits) {
++		uint trait;
++		prepared->traits = emalloc(sizeof(zend_class_entry*) * candidate->num_traits);
++		for (trait=0; trait<candidate->num_traits; trait++)
++			prepared->traits[trait] = pthreads_prepared_entry(thread, candidate->traits[trait]);
++		prepared->num_traits = candidate->num_traits;
++
++		if (candidate->trait_aliases) {
++			size_t alias = 0;
++
++			while (candidate->trait_aliases[alias]) {
++				alias++;
++			}
++			prepared->trait_aliases = emalloc(sizeof(zend_trait_alias*) * (alias+1));
++			alias = 0;
++
++			while (candidate->trait_aliases[alias]) {
++				prepared->trait_aliases[alias] = pthreads_preparation_copy_trait_alias(
++					thread, candidate->trait_aliases[alias]
++				);
++				alias++;
++			}
++			prepared->trait_aliases[alias]=NULL;
++		} else prepared->trait_aliases = NULL;
++
++		if (candidate->trait_precedences) {
++			size_t precedence = 0;
++
++            while (candidate->trait_precedences[precedence]) {
++                precedence++;
++            }
++            prepared->trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (precedence+1));
++            precedence = 0;
++
++            while (candidate->trait_precedences[precedence]) {
++	            prepared->trait_precedences[precedence] = pthreads_preparation_copy_trait_precedence(
++		            thread, candidate->trait_precedences[precedence]
++	            );
++	            precedence++;
++            }
++			prepared->trait_precedences[precedence]=NULL;
++		} else prepared->trait_precedences = NULL;
++	} else {
++		prepared->num_traits = 0;
++		prepared->trait_aliases = 0;
++		prepared->trait_precedences = 0;
++	}
++} /* }}} */
++
++/* {{{ */
++static zend_class_entry* pthreads_complete_entry(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared) {
++	prepared->ce_flags = candidate->ce_flags;
++
++	if (candidate->parent) {
++		prepared->parent = pthreads_prepared_entry(thread, candidate->parent);
++	}
++
++	if (candidate->num_interfaces) {
++		uint interface;
++		prepared->interfaces = emalloc(sizeof(zend_class_entry*) * candidate->num_interfaces);
++		for(interface=0; interface<candidate->num_interfaces; interface++)
++			prepared->interfaces[interface] = pthreads_prepared_entry(thread, candidate->interfaces[interface]);
++		prepared->num_interfaces = candidate->num_interfaces;
++	} else prepared->num_interfaces = 0;
++
++	prepare_class_traits(thread, candidate, prepared);
++
++	prepare_class_handlers(candidate, prepared);
++
++	// if this is an unbound anonymous class, then this will be the second copy,
++	// where all inherited functions will be copied
++	prepare_class_function_table(candidate, prepared);
++
++	prepare_class_interceptors(candidate, prepared);
++
++	return prepared;
++} /* }}} */
++
++/* {{{ */
++static zend_class_entry* pthreads_copy_entry(pthreads_object_t* thread, zend_class_entry *candidate) {
++	zend_class_entry *prepared;
++
++	prepared = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
++	prepared->name = zend_string_new(candidate->name);
++	prepared->type = candidate->type;
++
++	zend_initialize_class_data(prepared, 1);
++
++	prepared->ce_flags = candidate->ce_flags;
++	prepared->refcount = 1;
++
++	memcpy(&prepared->info.user, &candidate->info.user, sizeof(candidate->info.user));
++
++	if ((thread->options & PTHREADS_INHERIT_COMMENTS) &&
++	   (candidate->info.user.doc_comment)) {
++			prepared->info.user.doc_comment = zend_string_new(candidate->info.user.doc_comment);
++		} else prepared->info.user.doc_comment = NULL;
++
++	if (prepared->info.user.filename) {
++		zend_string *filename_copy;
++
++		if (!(filename_copy = zend_hash_find_ptr(&PTHREADS_ZG(filenames), candidate->info.user.filename))) {
++			filename_copy = zend_string_new(candidate->info.user.filename);
++			zend_hash_add_ptr(&PTHREADS_ZG(filenames), filename_copy, filename_copy);
++			zend_string_release(filename_copy);
++		}
++	}
++	prepare_class_property_table(thread, candidate, prepared);
++
++	if (candidate->ce_flags & ZEND_ACC_ANON_CLASS && !(prepared->ce_flags & ZEND_ACC_ANON_BOUND)) {
++
++		// this first copy will copy all declared functions on the unbound anonymous class
++		prepare_class_function_table(candidate, prepared);
++		prepare_class_interceptors(candidate, prepared);
++
++		return prepared;
++	}
++
++	return pthreads_complete_entry(thread, candidate, prepared);
++} /* }}} */
++
++/* {{{ */
++static inline int pthreads_prepared_entry_function_prepare(zval *bucket, int argc, va_list argv, zend_hash_key *key) {
++	zend_function *function = (zend_function*) Z_PTR_P(bucket);
++	pthreads_object_t* thread = va_arg(argv, pthreads_object_t*);	
++	zend_class_entry *prepared = va_arg(argv, zend_class_entry*);
++	zend_class_entry *candidate = va_arg(argv, zend_class_entry*);
++	zend_class_entry *scope = function->common.scope;
++	
++	if (function->type == ZEND_USER_FUNCTION) {
++		if (scope == candidate) {
++			function->common.scope = prepared;
++		} else {
++			if (function->common.scope->type == ZEND_USER_CLASS) {
++				function->common.scope = pthreads_prepared_entry(thread, function->common.scope);
++			}
++		}
++
++		/* runtime cache relies on immutable scope, so if scope changed, reallocate runtime cache */
++		/* IT WOULD BE NICE IF THIS WERE DOCUMENTED SOMEWHERE OTHER THAN PHP-SRC */
++		if (!function->op_array.run_time_cache || function->common.scope != scope) {
++			zend_op_array *op_array = &function->op_array;
++			op_array->run_time_cache = emalloc(op_array->cache_size);
++			memset(op_array->run_time_cache, 0, op_array->cache_size);
++			op_array->fn_flags |= ZEND_ACC_NO_RT_ARENA;
++		}
++	}
++	return ZEND_HASH_APPLY_KEEP;
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_closures(pthreads_object_t *thread) {
++	Bucket *bucket;
++
++	ZEND_HASH_FOREACH_BUCKET(PTHREADS_CG(thread->creator.ls, function_table), bucket) {
++		zend_function *function = Z_PTR(bucket->val),
++					  *prepared;
++		zend_string   *named;
++
++
++		if (function->common.fn_flags & ZEND_ACC_CLOSURE) {
++			if (zend_hash_exists(CG(function_table), bucket->key)) {
++				continue;
++			}
++
++			named = zend_string_new(bucket->key);
++			prepared = pthreads_copy_function(function);
++
++			if (!zend_hash_add_ptr(CG(function_table), named, prepared)) {
++				destroy_op_array((zend_op_array*) prepared);
++			}
++
++			zend_string_release(named);
++		}
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++zend_class_entry* pthreads_prepared_entry(pthreads_object_t* thread, zend_class_entry *candidate) {
++	return pthreads_create_entry(thread, candidate, 1);
++} /* }}} */
++
++/* {{{ */
++zend_class_entry* pthreads_create_entry(pthreads_object_t* thread, zend_class_entry *candidate, int do_late_bindings) {
++	zend_class_entry *prepared = NULL;
++	zend_string *lookup = NULL;
++
++	if (!candidate) {
++		return NULL;
++	}
++
++	if (candidate->type == ZEND_INTERNAL_CLASS) {
++		return zend_lookup_class(candidate->name);
++	}
++
++	lookup = zend_string_tolower(candidate->name);
++
++	if ((prepared = zend_hash_find_ptr(EG(class_table), lookup))) {
++	    zend_string_release(lookup);
++		
++		if(prepared->create_object == NULL && candidate->create_object != NULL) {
++			return pthreads_complete_entry(thread, candidate, prepared);
++		}
++		return prepared;
++	}
++
++	if (!(prepared = pthreads_copy_entry(thread, candidate))) {
++		zend_string_release(lookup);
++		return NULL;
++	}
++	zend_hash_update_ptr(EG(class_table), lookup, prepared);
++
++	if(do_late_bindings) {
++		pthreads_prepared_entry_late_bindings(thread, candidate, prepared);
++	}
++	pthreads_prepare_closures(thread);
++
++	zend_hash_apply_with_arguments(
++		&prepared->function_table, 
++		pthreads_prepared_entry_function_prepare, 
++		3, thread, prepared, candidate);	
++	
++	zend_string_release(lookup);
++
++	return prepared;
++} /* }}} */
++
++/* {{{ */
++void pthreads_prepared_entry_late_bindings(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared) {
++	prepare_class_statics(thread, candidate, prepared);
++	prepare_class_constants(thread, candidate, prepared);
++} /* }}} */
++
++
++/* {{{ */
++void pthreads_context_late_bindings(pthreads_object_t* thread) {
++	zend_class_entry *entry;
++	zend_string *name;
++
++	ZEND_HASH_FOREACH_STR_KEY_PTR(PTHREADS_CG(thread->local.ls, class_table), name, entry) {
++		if (entry->type != ZEND_INTERNAL_CLASS) {
++			pthreads_prepared_entry_late_bindings(thread, zend_hash_find_ptr(PTHREADS_CG(thread->creator.ls, class_table), name), entry);
++		}
++	} ZEND_HASH_FOREACH_END();
++}
++
++/* {{{ */
++static inline zend_bool pthreads_constant_exists(zend_string *name) {
++    int retval = 1;
++    zend_string *lookup;
++
++    if (!zend_hash_exists(EG(zend_constants), name)) {
++        lookup = zend_string_tolower(name);
++        retval = zend_hash_exists(EG(zend_constants), lookup);
++        zend_string_release(lookup);
++    }
++
++    return retval;
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_ini(pthreads_object_t* thread) {
++	zend_ini_entry *entry[2];
++	zend_string *name;
++	HashTable *table[2] = {PTHREADS_EG(thread->creator.ls, ini_directives), EG(ini_directives)};
++
++	if (!(thread->options & PTHREADS_ALLOW_HEADERS)) {
++		zend_alter_ini_entry_chars(
++			PTHREADS_G(strings).session.cache_limiter,
++			"nocache", sizeof("nocache")-1, 
++			PHP_INI_USER, PHP_INI_STAGE_ACTIVATE);
++		zend_alter_ini_entry_chars(
++			PTHREADS_G(strings).session.use_cookies,
++			"0", sizeof("0")-1,
++			PHP_INI_USER, PHP_INI_STAGE_ACTIVATE);
++	}	
++
++	ZEND_HASH_FOREACH_STR_KEY_PTR(table[0], name, entry[0]) {
++		if ((entry[1] = zend_hash_find_ptr(table[1], name))) {
++			if (entry[0]->value && entry[1]->value) {
++				if (strcmp(ZSTR_VAL(entry[0]->value), ZSTR_VAL(entry[1]->value)) != SUCCESS) {
++					zend_bool resmod = entry[1]->modifiable;
++					zend_string *copied = zend_string_new(name); 
++
++					if (!EG(modified_ini_directives)) {
++						ALLOC_HASHTABLE(EG(modified_ini_directives));
++						zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
++					}
++
++					if (!entry[1]->modified) {
++						entry[1]->orig_value = entry[1]->value;
++						entry[1]->orig_modifiable = entry[1]->modifiable;
++						entry[1]->modified = 1;
++						zend_hash_add_ptr(EG(modified_ini_directives), copied, entry[1]);
++					}
++
++					entry[1]->modifiable = 1;
++					entry[1]->on_modify(entry[1], entry[0]->value, entry[1]->mh_arg1, entry[1]->mh_arg2, entry[1]->mh_arg3, ZEND_INI_SYSTEM);
++					if (entry[1]->modified && entry[1]->orig_value != entry[1]->value) {
++						zend_string_release(entry[1]->value);
++					}
++					entry[1]->value = zend_string_new(entry[0]->value);			
++					entry[1]->modifiable = resmod;
++
++					zend_string_release(copied);
++				}
++			}
++		}
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_constants(pthreads_object_t* thread) {
++	zend_constant *zconstant;
++	zend_string *name;
++	
++	ZEND_HASH_FOREACH_STR_KEY_PTR(PTHREADS_EG(thread->creator.ls, zend_constants), name, zconstant) {
++		if (zconstant->name) {
++			if (strncmp(name->val, "STDIN", name->len-1)==0||
++				strncmp(name->val, "STDOUT", name->len-1)==0||
++				strncmp(name->val, "STDERR", name->len-1)==0){
++				continue;
++			} else {
++				zend_constant constant;
++
++				if (!pthreads_constant_exists(name)) {
++					constant.name = zend_string_new(name);
++
++					switch((Z_TYPE_INFO(constant.value)=Z_TYPE(zconstant->value))) {
++						case IS_TRUE:
++						case IS_FALSE:
++						case IS_LONG: {
++							Z_LVAL(constant.value)=Z_LVAL(zconstant->value);
++						} break;
++						case IS_DOUBLE: Z_DVAL(constant.value)=Z_DVAL(zconstant->value); break;
++						case IS_STRING: {
++							Z_STR(constant.value) = zend_string_new(Z_STR(zconstant->value));
++						} break;
++						case IS_ARRAY: {
++							pthreads_store_separate(&zconstant->value, &constant.value, 1);
++						} break;
++					}
++
++#if PHP_VERSION_ID < 70300
++					constant.flags = zconstant->flags;
++					constant.module_number = zconstant->module_number;
++#else
++					ZEND_CONSTANT_SET_FLAGS(&constant, ZEND_CONSTANT_FLAGS(zconstant), ZEND_CONSTANT_MODULE_NUMBER(zconstant));
++#endif
++					zend_register_constant(&constant);
++				}
++			}
++		}
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_functions(pthreads_object_t* thread) {
++	zend_string *key, *name;
++	zend_function *value = NULL, *prepared = NULL;
++
++	ZEND_HASH_FOREACH_STR_KEY_PTR(PTHREADS_CG(thread->creator.ls, function_table), key, value) {
++		if (value->type == ZEND_INTERNAL_FUNCTION ||
++			zend_hash_exists(PTHREADS_CG(thread->local.ls, function_table), key))
++			continue;
++
++		name = zend_string_new(key);
++		prepared = pthreads_copy_function(value);
++
++		if (!zend_hash_add_ptr(CG(function_table), name, prepared)) {
++			destroy_op_array((zend_op_array*)prepared);
++		}
++
++		zend_string_release(name);
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_classes(pthreads_object_t* thread) {
++	zend_class_entry *entry;
++	zend_string *name;
++
++	ZEND_HASH_FOREACH_STR_KEY_PTR(PTHREADS_CG(thread->creator.ls, class_table), name, entry) {
++		if (!zend_hash_exists(PTHREADS_CG(thread->local.ls, class_table), name) && ZSTR_VAL(name)[0] != '\0') {
++			pthreads_create_entry(thread, entry, 0);
++		}
++	} ZEND_HASH_FOREACH_END();
++
++	pthreads_context_late_bindings(thread);
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_includes(pthreads_object_t* thread) {
++	zend_string *file;
++	ZEND_HASH_FOREACH_STR_KEY(&PTHREADS_EG(thread->creator.ls, included_files), file) {
++		zend_string *name = zend_string_new(file);
++		zend_hash_add_empty_element(&EG(included_files), name);
++		zend_string_release(name);
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_exception_handler(pthreads_object_t* thread) {
++	if (thread->user_exception_handler != NULL) {
++		pthreads_store_convert(thread->user_exception_handler, &EG(user_exception_handler));
++		pthreads_store_storage_dtor(thread->user_exception_handler);
++		thread->user_exception_handler = NULL;
++	}
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_resource_destructor(pthreads_object_t* thread) {
++	if (!PTHREADS_G(default_resource_dtor))
++		PTHREADS_G(default_resource_dtor)=(EG(regular_list).pDestructor);
++	EG(regular_list).pDestructor =  (dtor_func_t) pthreads_prepared_resource_dtor;	
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_prepare_sapi(pthreads_object_t* thread) {
++	SG(sapi_started) = 0;
++
++	if (!(thread->options & PTHREADS_ALLOW_HEADERS)) {
++		SG(headers_sent)=1;
++		SG(request_info).no_headers = 1;
++	}
++} /* }}} */
++
++/* {{{ */
++static inline void pthreads_rebuild_object(zval *zv) {
++	if (Z_TYPE_P(zv) == IS_OBJECT) {
++		rebuild_object_properties(Z_OBJ_P(zv));
++	} else if (Z_TYPE_P(zv) == IS_ARRAY) {
++		zval *object = zend_hash_index_find(Z_ARRVAL_P(zv), 0);
++		if (object && Z_TYPE_P(object) == IS_OBJECT) {
++			rebuild_object_properties(Z_OBJ_P(object));
++		}
++	}
++} /* }}} */
++
++/* {{{ */
++void pthreads_prepare_parent(pthreads_object_t *thread) {
++	zval *handler = &EG(user_exception_handler);
++
++	if (thread->options & (PTHREADS_INHERIT_CLASSES | PTHREADS_INHERIT_FUNCTIONS)) {
++		if (Z_TYPE_P(handler) != IS_UNDEF) {
++			pthreads_rebuild_object(handler);
++			if (Z_TYPE_P(handler) == IS_ARRAY) {
++				if (zend_hash_num_elements(Z_ARRVAL_P(handler)) > 1) {
++					if (!(thread->options & PTHREADS_INHERIT_CLASSES)) {
++						return;
++					}
++				} else if (!(thread->options & PTHREADS_INHERIT_FUNCTIONS)) {
++					return;
++				}
++			}
++
++			thread->user_exception_handler = pthreads_store_create(handler, 1);
++		}
++	}
++} /* }}} */
++
++/* {{{ */
++int pthreads_prepared_startup(pthreads_object_t* thread, pthreads_monitor_t *ready) {
++
++	PTHREADS_PREPARATION_BEGIN_CRITICAL() {
++		thread->local.id = pthreads_self();
++		thread->local.ls = ts_resource(0);
++		TSRMLS_CACHE_UPDATE();
++
++		SG(server_context) = 
++			PTHREADS_SG(thread->creator.ls, server_context);
++
++		PG(expose_php) = 0;
++		PG(auto_globals_jit) = 0;
++
++		php_request_startup();
++		PG(during_request_startup) = 0;
++
++		pthreads_prepare_sapi(thread);
++	
++		if (thread->options & PTHREADS_INHERIT_INI)
++			pthreads_prepare_ini(thread);
++
++		if (thread->options & PTHREADS_INHERIT_CONSTANTS)
++			pthreads_prepare_constants(thread);
++
++		if (thread->options & PTHREADS_INHERIT_FUNCTIONS)
++			pthreads_prepare_functions(thread);
++		else pthreads_prepare_closures(thread);
++
++		if (thread->options & PTHREADS_INHERIT_CLASSES) {
++			pthreads_prepare_classes(thread);
++		} else {
++			pthreads_create_entry(thread, thread->std.ce, 0);
++			pthreads_context_late_bindings(thread);
++		}
++
++		if (thread->options & PTHREADS_INHERIT_INCLUDES)
++			pthreads_prepare_includes(thread);
++
++		pthreads_prepare_exception_handler(thread);
++		pthreads_prepare_resource_destructor(thread);
++		pthreads_monitor_add(ready, PTHREADS_MONITOR_READY);
++	} PTHREADS_PREPARATION_END_CRITICAL();
++
++	return SUCCESS;
++} /* }}} */
++
++/* {{{ */
++static inline int pthreads_resources_cleanup(zval *bucket) {
++	if (pthreads_resources_kept(Z_RES_P(bucket))) {
++		return ZEND_HASH_APPLY_REMOVE;
++	} else return ZEND_HASH_APPLY_KEEP;
++} /* }}} */
++
++/* {{{ */
++int pthreads_prepared_shutdown(void) {
++	PTHREADS_PREPARATION_BEGIN_CRITICAL() {
++		zend_hash_apply(&EG(regular_list), pthreads_resources_cleanup);
++
++		PG(report_memleaks) = 0;
++
++		php_request_shutdown((void*)NULL);
++
++		ts_free_thread();
++	} PTHREADS_PREPARATION_END_CRITICAL();
++
++	return SUCCESS;
++} /* }}} */
++
++/* {{{ */
++static zend_trait_alias * pthreads_preparation_copy_trait_alias(pthreads_object_t* thread, zend_trait_alias *alias) {
++	zend_trait_alias *copy = ecalloc(1, sizeof(zend_trait_alias));
++
++#if PHP_VERSION_ID >= 70300
++	pthreads_preparation_copy_trait_method_reference(thread, &alias->trait_method, &copy->trait_method);
++#else
++	if (alias->trait_method) {
++		copy->trait_method = pthreads_preparation_copy_trait_method_reference(thread, alias->trait_method);
++	}
++#endif
++
++	if (alias->alias) {
++		copy->alias = zend_string_new(alias->alias);
++	}
++
++	copy->modifiers = alias->modifiers;
++
++	return copy;
++} /* }}} */
++
++/* {{{ */
++static zend_trait_precedence * pthreads_preparation_copy_trait_precedence(pthreads_object_t* thread, zend_trait_precedence *precedence) {
++	zend_trait_precedence *copy;
++#if PHP_VERSION_ID >= 70300
++	copy = ecalloc(1, sizeof(zend_trait_precedence) + (precedence->num_excludes - 1) * sizeof(zend_string *));
++
++	pthreads_preparation_copy_trait_method_reference(thread, &precedence->trait_method, &copy->trait_method);
++	copy->num_excludes = precedence->num_excludes;
++	int i;
++	for (i = 0; i < precedence->num_excludes; ++i) {
++		copy->exclude_class_names[i] = zend_string_new(precedence->exclude_class_names[i]);
++	}
++#else
++	copy = ecalloc(1, sizeof(zend_trait_precedence));
++
++	copy->trait_method = pthreads_preparation_copy_trait_method_reference(thread, precedence->trait_method);
++	if (precedence->exclude_from_classes) {
++		copy->exclude_from_classes = emalloc(sizeof(*copy->exclude_from_classes));
++		copy->exclude_from_classes->ce = pthreads_prepared_entry(
++			thread, precedence->exclude_from_classes->ce
++		);
++		copy->exclude_from_classes->class_name = zend_string_new(precedence->exclude_from_classes->class_name);
++	}
++#endif
++
++	return copy;
++} /* }}} */
++
++#if PHP_VERSION_ID >= 70300
++/* {{{ */
++static void pthreads_preparation_copy_trait_method_reference(pthreads_object_t* thread, zend_trait_method_reference *reference, zend_trait_method_reference *copy) {
++	if (reference->method_name) {
++		copy->method_name = zend_string_new(reference->method_name);
++	}
++	if (reference->class_name) {
++		copy->class_name = zend_string_new(reference->class_name);
++	}
++} /* }}} */
++#else
++/* {{{ */
++static zend_trait_method_reference * pthreads_preparation_copy_trait_method_reference(pthreads_object_t* thread, zend_trait_method_reference *reference) {
++	zend_trait_method_reference *copy = ecalloc(1, sizeof(zend_trait_method_reference));
++
++	copy->method_name = zend_string_new(reference->method_name);
++	if (reference->class_name) {
++		copy->class_name = zend_string_new(reference->class_name);
++	}
++	copy->ce = pthreads_prepared_entry(thread, (zend_class_entry*)reference->ce);
++
++	return copy;
++} /* }}} */
++#endif
++
++/* {{{ */
++static void pthreads_prepared_resource_dtor(zval *zv) {
++	zend_try {
++		if (!pthreads_resources_kept(Z_RES_P(zv)) && PTHREADS_G(default_resource_dtor)){
++			PTHREADS_G(default_resource_dtor)(zv);
++		}
++	} zend_end_try();
++} /* }}} */
++#endif
++
+diff --git a/ext/pthreads/src/prepare.h b/ext/pthreads/src/prepare.h
+new file mode 100644
+index 00000000..a8bb949b
+--- /dev/null
++++ b/ext/pthreads/src/prepare.h
+@@ -0,0 +1,45 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_PREPARE_H
++#define HAVE_PTHREADS_PREPARE_H
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++/* {{{ fetch prepared class entry */
++zend_class_entry* pthreads_prepared_entry(pthreads_object_t* thread, zend_class_entry *candidate); /* }}} */
++
++/* {{{ */
++zend_class_entry* pthreads_create_entry(pthreads_object_t* thread, zend_class_entry *candidate, int do_late_bindings); /* }}} */
++
++/* {{{ */
++void pthreads_prepared_entry_late_bindings(pthreads_object_t* thread, zend_class_entry *candidate, zend_class_entry *prepared); /* }}} */
++
++/* {{{ */
++void pthreads_context_late_bindings(pthreads_object_t* thread); /* }}} */
++
++/* {{{ */
++void pthreads_prepare_parent(pthreads_object_t *thread); /* }}} */
++
++/* {{{ */
++int pthreads_prepared_startup(pthreads_object_t* thread, pthreads_monitor_t *ready); /* }}} */
++
++/* {{{ */
++int pthreads_prepared_shutdown(void); /* }}} */
++#endif
+diff --git a/ext/pthreads/src/pthreads.h b/ext/pthreads/src/pthreads.h
+new file mode 100644
+index 00000000..d8f4771a
+--- /dev/null
++++ b/ext/pthreads/src/pthreads.h
+@@ -0,0 +1,194 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_H
++#define HAVE_PTHREADS_H
++
++#ifdef HAVE_CONFIG_H
++# include "config.h"
++#endif
++
++#include <stdio.h>
++#ifndef _WIN32
++#include <pthread.h>
++#include <sys/time.h>
++#include <signal.h>
++#else
++#define HAVE_STRUCT_TIMESPEC
++#include <win32/time.h>
++#include <pthread.h>
++#include <signal.h>
++#endif
++
++#include <php.h>
++#include <php_globals.h>
++#include <php_main.h>
++#include <php_network.h>
++#ifndef _WIN32
++#include <netdb.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <sys/un.h>
++#	ifdef HAVE_IF_NAMETOINDEX
++#		include <net/if.h>
++#	endif
++#endif
++
++#include <php_ticks.h>
++#include <ext/standard/info.h>
++#include <ext/standard/basic_functions.h>
++#include <ext/standard/php_var.h>
++#include <ext/spl/spl_exceptions.h>
++#include <ext/spl/spl_iterators.h>
++#include <Zend/zend.h>
++#include <Zend/zend_closures.h>
++#include <Zend/zend_compile.h>
++#include <Zend/zend_exceptions.h>
++#include <Zend/zend_extensions.h>
++#include <Zend/zend_globals.h>
++#include <Zend/zend_hash.h>
++#include <Zend/zend_ts_hash.h>
++#include <Zend/zend_interfaces.h>
++#include <Zend/zend_inheritance.h>
++#include <Zend/zend_list.h>
++#include <Zend/zend_object_handlers.h>
++#include <Zend/zend_smart_str.h>
++#include <Zend/zend_variables.h>
++#include <Zend/zend_vm.h>
++#include <TSRM/TSRM.h>
++
++#ifdef DMALLOC
++#include <dmalloc.h>
++#endif
++
++extern zend_class_entry *pthreads_threaded_entry;
++extern zend_class_entry *pthreads_volatile_entry;
++extern zend_class_entry *pthreads_thread_entry;
++extern zend_class_entry *pthreads_worker_entry;
++extern zend_class_entry *pthreads_socket_entry;
++
++#ifndef IS_PTHREADS_CLASS
++#define IS_PTHREADS_CLASS(c) \
++	(instanceof_function(c, pthreads_threaded_entry))
++#endif
++
++#ifndef IS_PTHREADS_OBJECT
++#define IS_PTHREADS_OBJECT(o)   \
++        (Z_TYPE_P(o) == IS_OBJECT && IS_PTHREADS_CLASS(Z_OBJCE_P(o)))
++#endif
++
++#ifndef IS_PTHREADS_VOLATILE
++#define IS_PTHREADS_VOLATILE(o)   \
++        (Z_TYPE_P(o) == IS_OBJECT && instanceof_function(Z_OBJCE_P(o), pthreads_volatile_entry))
++#endif
++
++#ifndef IS_PTHREADS_CLOSURE
++#define IS_PTHREADS_CLOSURE(z) \
++	(Z_TYPE_P(z) == IS_OBJECT && instanceof_function(Z_OBJCE_P(z), zend_ce_closure))
++#endif
++
++extern zend_object_handlers pthreads_handlers;
++extern zend_object_handlers pthreads_socket_handlers;
++extern zend_object_handlers *zend_handlers;
++
++extern struct _pthreads_globals pthreads_globals;
++
++ZEND_EXTERN_MODULE_GLOBALS(pthreads)
++
++#ifndef PTHREADS_ZG
++ZEND_BEGIN_MODULE_GLOBALS(pthreads)
++	pid_t pid;
++	int   signal;
++	zval  this;
++	HashTable resolve;
++	HashTable filenames;
++	HashTable *resources;
++	int hard_copy_interned_strings;
++ZEND_END_MODULE_GLOBALS(pthreads)
++#	define PTHREADS_ZG(v) TSRMG(pthreads_globals_id, zend_pthreads_globals *, v)
++#   define PTHREADS_PID() PTHREADS_ZG(pid) ? PTHREADS_ZG(pid) : (PTHREADS_ZG(pid)=getpid())
++#endif
++
++#define PTHREADS_FETCH_ALL(ls, id, type) ((type) (*((void ***) ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])
++#define PTHREADS_FETCH_CTX(ls, id, type, element) (((type) (*((void ***) ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
++#define PTHREADS_CG(ls, v) PTHREADS_FETCH_CTX(ls, compiler_globals_id, zend_compiler_globals*, v)
++#define PTHREADS_CG_ALL(ls) PTHREADS_FETCH_ALL(ls, compiler_globals_id, zend_compiler_globals*)
++#define PTHREADS_EG(ls, v) PTHREADS_FETCH_CTX(ls, executor_globals_id, zend_executor_globals*, v)
++#define PTHREADS_SG(ls, v) PTHREADS_FETCH_CTX(ls, sapi_globals_id, sapi_globals_struct*, v)
++#define PTHREADS_PG(ls, v) PTHREADS_FETCH_CTX(ls, core_globals_id, php_core_globals*, v)
++#define PTHREADS_EG_ALL(ls) PTHREADS_FETCH_ALL(ls, executor_globals_id, zend_executor_globals*)
++
++static zend_string *zend_string_new(zend_string *s)
++{
++	if (ZSTR_IS_INTERNED(s)) {
++		if (!PTHREADS_ZG(hard_copy_interned_strings)) {
++			return s;
++		}
++		return zend_new_interned_string(zend_string_init(ZSTR_VAL(s), ZSTR_LEN(s), GC_FLAGS(s) & IS_STR_PERSISTENT));
++	}
++	return zend_string_dup(s, GC_FLAGS(s) & IS_STR_PERSISTENT);
++}
++
++/* {{{ */
++static inline const zend_op* pthreads_check_opline(zend_execute_data *ex, zend_long offset, zend_uchar opcode) {
++	if (ex && ex->func && ex->func->type == ZEND_USER_FUNCTION) {
++		zend_op_array *ops = &ex->func->op_array;
++		const zend_op *opline = ex->opline;
++
++		if ((opline + offset) >= ops->opcodes) {
++			opline += offset;
++			if (opline->opcode == opcode) {
++				return opline;			
++			}
++		}
++	}
++	return NULL;
++} /* }}} */
++
++/* {{{ */
++static inline zend_bool pthreads_check_opline_ex(zend_execute_data *ex, zend_long offset, zend_uchar opcode, uint32_t extended_value) {
++	const zend_op *opline = pthreads_check_opline(ex, offset, opcode);
++	if (opline && opline->extended_value == extended_value)
++		return 1;
++	return 0;
++} /* }}} */
++
++/* {{{ */
++typedef struct _pthreads_call_t {
++	zend_fcall_info fci;
++	zend_fcall_info_cache fcc;
++} pthreads_call_t; /* }}} */
++
++#define PTHREADS_CALL_EMPTY {empty_fcall_info, empty_fcall_info_cache}
++
++#ifndef HAVE_PTHREADS_MONITOR_H
++#	include <src/monitor.h>
++#endif
++
++#ifndef HAVE_PTHREADS_STACK_H
++#	include <src/stack.h>
++#endif
++
++#ifndef HAVE_PTHREADS_STORE_H
++#	include <src/store.h>
++#endif
++
++#ifndef HAVE_PTHREADS_THREAD_H
++#	include <src/thread.h>
++#endif
++
++#endif
+diff --git a/ext/pthreads/src/resources.c b/ext/pthreads/src/resources.c
+new file mode 100644
+index 00000000..14bb8f22
+--- /dev/null
++++ b/ext/pthreads/src/resources.c
+@@ -0,0 +1,56 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_RESOURCES
++#define HAVE_PTHREADS_RESOURCES
++
++/*
++* THIS IS A TEST | THIS IS A TEST | THIS IS A TEST | THIS IS A TEST
++* IF IT DOESN'T WORK TOUGH SHIT !! | IF IT DOESN'T WORK TOUGH SHIT !!
++*/
++
++#ifndef HAVE_PTHREADS_RESOURCES_H
++#	include <src/resources.h>
++#endif
++
++/* {{{ mark a resource for keeping */
++zend_bool pthreads_resources_keep(pthreads_resource res) {
++	if (!PTHREADS_ZG(resources)) {
++		ALLOC_HASHTABLE(PTHREADS_ZG(resources));
++		zend_hash_init(PTHREADS_ZG(resources), 15, NULL, NULL, 0);
++	}
++	
++	if (zend_hash_index_update_ptr(PTHREADS_ZG(resources), (zend_long) res->original, res)) {
++		return 1;
++	}
++	return 0;
++} /* }}} */
++
++/* {{{ tells if a resource is being kept */
++zend_bool pthreads_resources_kept(zend_resource *entry) {
++	if (PTHREADS_ZG(resources)) {
++		pthreads_resource data = zend_hash_index_find_ptr(PTHREADS_ZG(resources), (zend_long) entry);
++		if (data) {
++			if (data->ls != TSRMLS_CACHE) {
++				return 1;
++			}
++		}
++	}
++	return 0;
++} /* }}} */
++#endif
++
+diff --git a/ext/pthreads/src/resources.h b/ext/pthreads/src/resources.h
+new file mode 100644
+index 00000000..08ba386a
+--- /dev/null
++++ b/ext/pthreads/src/resources.h
+@@ -0,0 +1,33 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_RESOURCES_H
++#define HAVE_PTHREADS_RESOURCES_H
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++typedef struct {
++	zend_resource *original;
++	void ***ls;
++} *pthreads_resource;
++
++zend_bool pthreads_resources_keep(pthreads_resource data);
++zend_bool pthreads_resources_kept(zend_resource *entry);
++#endif
++
+diff --git a/ext/pthreads/src/socket.c b/ext/pthreads/src/socket.c
+new file mode 100644
+index 00000000..3218a926
+--- /dev/null
++++ b/ext/pthreads/src/socket.c
+@@ -0,0 +1,1066 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_SOCKET
++#define HAVE_PTHREADS_SOCKET
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++#ifndef HAVE_PTHREADS_SOCKET_H
++#	include <src/socket.h>
++#endif
++
++#ifdef PHP_WIN32
++# include "sockets/windows_common.h"
++# define SOCK_EINVAL WSAEINVAL
++#else
++# include <fcntl.h>
++# include <errno.h>
++# define set_errno(a) (errno = a)
++# define SOCK_EINVAL EINVAL
++#endif
++
++#ifndef PHP_WIN32
++#define PTHREADS_INVALID_SOCKET -1
++#define PTHREADS_IS_INVALID_SOCKET(sock) ((sock)->fd < 0)
++#define PTHREADS_CLOSE_SOCKET_INTERNAL(sock) close((sock)->fd)
++#else
++#define PTHREADS_INVALID_SOCKET INVALID_SOCKET
++#define PTHREADS_IS_INVALID_SOCKET(sock) ((sock)->fd == INVALID_SOCKET)
++#define PTHREADS_CLOSE_SOCKET_INTERNAL(sock) closesocket((sock)->fd)
++#endif
++
++#define PTHREADS_IS_VALID_SOCKET(sock) !PTHREADS_IS_INVALID_SOCKET(sock)
++#define PTHREADS_CLEAR_SOCKET_ERROR(sock) (sock)->error = SUCCESS
++
++#define PTHREADS_SOCKET_CHECK(sock) do { \
++	if (PTHREADS_IS_INVALID_SOCKET(sock)) { \
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0, \
++			"socket found in invalid state"); \
++		return; \
++	} \
++} while(0)
++
++#define PTHREADS_SOCKET_CHECK_EX(sock, retval) do { \
++	if (PTHREADS_IS_INVALID_SOCKET(sock)) { \
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0, \
++			"socket found in invalid state"); \
++		return (retval); \
++	} \
++} while(0)
++
++#define PTHREADS_HANDLE_SOCKET_ERROR(eno, msg) do { \
++	if ((eno) != EAGAIN && (eno) != EWOULDBLOCK && (eno) != EINPROGRESS && (eno) != SOCK_EINVAL) { \
++		char *estr = (eno) != SUCCESS ? \
++			php_socket_strerror((eno), NULL, 0) : \
++			NULL; \
++		zend_throw_exception_ex(spl_ce_RuntimeException, (eno), \
++			"%s (%d): %s", (msg), (eno), estr ? estr : "unknown"); \
++		if ((eno) != SUCCESS) { \
++			if (estr) { \
++				efree(estr); \
++			} \
++		} \
++	} \
++	\
++} while(0)
++
++#define PTHREADS_SOCKET_ERROR(socket, msg, eno) do { \
++	(socket)->error = (eno); \
++	PTHREADS_HANDLE_SOCKET_ERROR(eno, msg); \
++} while(0)
++
++pthreads_socket_t* pthreads_socket_alloc(void) {
++	return (pthreads_socket_t*) ecalloc(1, sizeof(pthreads_socket_t));
++}
++
++void pthreads_socket_construct(zval *object, zend_long domain, zend_long type, zend_long protocol) {
++	pthreads_object_t *threaded = 
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	threaded->store.sock->fd = socket(domain, type, protocol);
++
++	if (PTHREADS_IS_VALID_SOCKET(threaded->store.sock)) {
++		threaded->store.sock->domain = domain;
++		threaded->store.sock->type = type;
++		threaded->store.sock->protocol = protocol;
++		threaded->store.sock->error = SUCCESS;
++		return;
++	}
++
++	PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to create socket", errno);
++}
++
++void pthreads_socket_set_option(zval *object, zend_long level, zend_long name, zend_long value, zval *return_value) {
++	pthreads_object_t *threaded = 
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (setsockopt(threaded->store.sock->fd, level, name, (char*) &value, sizeof(value)) != SUCCESS) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to set socket option", errno);
++
++		RETURN_FALSE;
++	}
++
++	RETURN_TRUE;
++}
++
++void pthreads_socket_get_option(zval *object, zend_long level, zend_long name, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	socklen_t unused = sizeof(zend_long);
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (getsockopt(threaded->store.sock->fd, level, name, (void*) &Z_LVAL_P(return_value), &unused) != SUCCESS) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to retrieve socket option", errno);
++
++		RETURN_FALSE;
++	} else {
++		Z_TYPE_INFO_P(return_value) = IS_LONG;
++	}
++}
++
++static inline zend_bool pthreads_socket_set_inet_addr(pthreads_socket_t *sock, struct sockaddr_in *sin, zend_string *address) {
++	struct in_addr tmp;
++	struct hostent *hentry;
++
++	if (inet_aton(ZSTR_VAL(address), &tmp)) {
++		sin->sin_addr.s_addr = tmp.s_addr;
++	} else {
++		if (ZSTR_LEN(address) > MAXFQDNLEN || !(hentry = php_network_gethostbyname(ZSTR_VAL(address)))) {
++			PTHREADS_SOCKET_ERROR(sock, "Host lookup failed", errno);
++
++			return 0;
++		}
++
++		if (hentry->h_addrtype != AF_INET) {
++			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++					"Host lookup failed: Non AF_INET domain returned on AF_INET socket");
++			return 0;
++		}
++		
++		memcpy(&(sin->sin_addr.s_addr), hentry->h_addr_list[0], hentry->h_length);	
++	}
++
++	return 1;
++}
++
++static inline int pthreads_socket_string_to_if_index(const char *val, unsigned *out) {
++#if HAVE_IF_NAMETOINDEX
++	unsigned int ind = if_nametoindex(val);
++
++	if (ind != 0) {
++		*out = ind;
++		return SUCCESS;
++	}
++	
++	return FAILURE;
++#else
++	/* throw */
++	return FAILURE;
++#endif
++}
++
++static inline zend_bool pthreads_socket_set_inet6_addr(pthreads_socket_t *sock, struct sockaddr_in6 *sin, zend_string *address) {
++	struct in6_addr tmp;
++#if HAVE_GETADDRINFO
++	struct addrinfo hints;
++	struct addrinfo *addrinfo = NULL;	
++#endif
++	char *scope = strchr(ZSTR_VAL(address), '%');
++	
++	if (inet_pton(AF_INET6, ZSTR_VAL(address), &tmp)) {
++		memcpy(&(sin->sin6_addr.s6_addr), &(tmp.s6_addr), sizeof(struct in6_addr));
++	} else {
++#if HAVE_GETADDRINFO
++		memset(&hints, 0, sizeof(struct addrinfo));
++		
++		hints.ai_family = AF_INET6;
++#if HAVE_AI_V4MAPPED
++		hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
++#else
++		hints.ai_flags = AI_ADDRCONFIG;
++#endif
++		getaddrinfo(ZSTR_VAL(address), NULL, &hints, &addrinfo);
++		if (!addrinfo) {
++			PTHREADS_SOCKET_ERROR(sock, "Host lookup failed", errno);
++			return 0;
++		}
++
++		if (addrinfo->ai_family != PF_INET6 || addrinfo->ai_addrlen != sizeof(struct sockaddr_in6)) {
++			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++					"Host lookup failed: Non AF_INET6 domain returned on AF_INET6 socket");
++
++			freeaddrinfo(addrinfo);
++			return 0;
++		}
++
++		memcpy(&(sin->sin6_addr.s6_addr), ((struct sockaddr_in6*)(addrinfo->ai_addr))->sin6_addr.s6_addr, sizeof(struct in6_addr));
++		freeaddrinfo(addrinfo);
++#else
++		/* No IPv6 specific hostname resolution is available on this system? */
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Host lookup failed: getaddrinfo() not available on this system");
++
++		return 0;
++#endif
++
++		if (scope++) {
++			zend_long lval = 0;
++			double dval = 0;
++			unsigned scope_id = 0;
++
++			if (IS_LONG == is_numeric_string(scope, strlen(scope), &lval, &dval, 0)) {
++				if (lval > 0 && lval <= UINT_MAX) {
++					scope_id = lval;
++				} else {
++					pthreads_socket_string_to_if_index(scope, &scope_id);
++				}
++				sin->sin6_scope_id = scope_id;
++			}
++		}
++	}
++
++	return 1;
++}
++
++void pthreads_socket_bind(zval *object, zend_string *address, zend_long port, zval *return_value) {
++	pthreads_object_t	*threaded =
++			PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	php_sockaddr_storage	sa_storage = {0};
++	struct sockaddr		*sock_type = (struct sockaddr*) &sa_storage;
++	zend_long		retval = 0;
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	switch (threaded->store.sock->domain) {
++#ifndef _WIN32
++		case AF_UNIX: {
++			struct sockaddr_un *sa = (struct sockaddr_un *) sock_type;
++
++			sa->sun_family = AF_UNIX;
++
++			if (ZSTR_LEN(address) >= sizeof(sa->sun_path)) {
++				zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++						"Invalid path: too long (maximum size is %d)",
++						(int)sizeof(sa->sun_path) - 1);
++				return;
++			}
++
++			memcpy(&sa->sun_path, ZSTR_VAL(address), ZSTR_LEN(address));
++
++			retval = bind(threaded->store.sock->fd, (struct sockaddr *) sa,
++					offsetof(struct sockaddr_un, sun_path) + ZSTR_LEN(address));
++		} break;
++#endif
++		case AF_INET: {
++			struct sockaddr_in *sa = (struct sockaddr_in *) sock_type;
++
++			sa->sin_family = AF_INET;
++			sa->sin_port = htons((unsigned short) port);
++			
++			if (!pthreads_socket_set_inet_addr(threaded->store.sock, sa, address)) {
++				RETURN_FALSE;
++			}
++
++			retval = bind(threaded->store.sock->fd, (struct sockaddr *)sa, sizeof(struct sockaddr_in));
++		} break;
++
++#if HAVE_IPV6
++		case AF_INET6: {
++			struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_type;
++
++			sa->sin6_family = AF_INET6;
++			sa->sin6_port = htons((unsigned short) port);
++			
++			if (!pthreads_socket_set_inet6_addr(threaded->store.sock, sa, address)) {
++				RETURN_FALSE;
++			}
++
++			retval = bind(threaded->store.sock->fd, (struct sockaddr *)sa, sizeof(struct sockaddr_in6));
++		} break;
++#endif
++	}
++
++	if (retval != SUCCESS) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to bind address", errno);
++		RETURN_FALSE;
++	}
++
++	RETURN_TRUE;
++}
++
++void pthreads_socket_listen(zval *object, zend_long backlog, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (listen(threaded->store.sock->fd, backlog) != SUCCESS) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to listen on socket", errno);
++
++		RETURN_FALSE;
++	}
++
++	RETURN_TRUE;
++}
++
++void pthreads_socket_accept(zval *object, zend_class_entry *ce, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	pthreads_object_t *accepted;
++
++	php_sockaddr_storage sa;
++	socklen_t            sa_len = sizeof(sa);
++	zend_bool	     blocking = threaded->store.sock->blocking;
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (!instanceof_function(ce, pthreads_socket_entry)) {
++		zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++			"%s is not an instance of Socket",
++			ZSTR_VAL(ce->name));
++		return;
++	}
++	php_socket_t acceptedFd = accept(threaded->store.sock->fd, (struct sockaddr*) &sa, &sa_len);
++
++	if(acceptedFd < 0) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to accept on socket", errno);
++
++		RETURN_FALSE;
++	}
++	object_init_ex(return_value, ce);
++
++	accepted = PTHREADS_FETCH_FROM(Z_OBJ_P(return_value));
++	accepted->store.sock->fd = acceptedFd;
++	accepted->store.sock->blocking = 1;
++	accepted->store.sock->domain = ((struct sockaddr*) &sa)->sa_family;
++}
++
++void pthreads_socket_connect(zval *object, int argc, zend_string *address, zend_long port, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	int retval;
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	switch (threaded->store.sock->domain) {
++#if HAVE_IPV6
++		case AF_INET6: {
++			struct sockaddr_in6 sin6 = {0};
++
++			if (argc != 2) {
++				zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Socket of type AF_INET6 requires 2 arguments");
++				return;
++			}
++
++			memset(&sin6, 0, sizeof(struct sockaddr_in6));
++
++			sin6.sin6_family = AF_INET6;
++			sin6.sin6_port   = htons((unsigned short int)port);
++
++			if (!pthreads_socket_set_inet6_addr(threaded->store.sock, &sin6, address)) {
++				RETURN_FALSE;
++			}
++
++			retval = connect(threaded->store.sock->fd, (struct sockaddr *)&sin6, sizeof(struct sockaddr_in6));
++		} break;
++#endif
++
++		case AF_INET: {
++			struct sockaddr_in sin = {0};
++
++			if (argc != 2) {
++				zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Socket of type AF_INET requires 2 arguments");
++				return;
++			}
++
++			sin.sin_family = AF_INET;
++			sin.sin_port   = htons((unsigned short int)port);
++			
++			if (!pthreads_socket_set_inet_addr(threaded->store.sock, &sin, address)) {
++				RETURN_FALSE;
++			}
++
++			retval = connect(threaded->store.sock->fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in));
++		} break;
++
++#ifndef _WIN32
++		case AF_UNIX: {
++			struct sockaddr_un s_un = {0};
++
++			if (ZSTR_LEN(address) >= sizeof(s_un.sun_path)) {
++				zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Path too long");
++				return;
++			}
++
++			s_un.sun_family = AF_UNIX;
++			memcpy(&s_un.sun_path, ZSTR_VAL(address), ZSTR_LEN(address));
++
++			retval = connect(threaded->store.sock->fd, (struct sockaddr *) &s_un,
++					(socklen_t)(XtOffsetOf(struct sockaddr_un, sun_path) + ZSTR_LEN(address)));
++		} break;
++#endif
++		default:
++			RETURN_FALSE;
++	}
++
++	if (retval != SUCCESS) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "unable to connect", errno);
++		RETURN_FALSE;
++	}
++
++	RETURN_TRUE;
++}
++
++static int pthreads_normal_read(pthreads_object_t *threaded, void *buf, size_t maxlen, int flags) /* {{{ */
++{
++	int m = 0;
++	size_t n = 0;
++	int no_read = 0;
++	int nonblock = 0;
++	char *t = (char *) buf;
++
++	PTHREADS_SOCKET_CHECK_EX(threaded->store.sock, -1);
++
++#ifndef PHP_WIN32
++	m = fcntl(threaded->store.sock->fd, F_GETFL);
++	if (m < 0) {
++		return m;
++	}
++	nonblock = (m & O_NONBLOCK);
++	m = 0;
++#else
++	nonblock = !threaded->store.sock->blocking;
++#endif
++	set_errno(0);
++
++	*t = '\0';
++	while (*t != '\n' && *t != '\r' && n < maxlen) {
++		if (m > 0) {
++			t++;
++			n++;
++		} else if (m == 0) {
++			no_read++;
++			if (nonblock && no_read >= 2) {
++				return n;
++				/* The first pass, m always is 0, so no_read becomes 1
++				 * in the first pass. no_read becomes 2 in the second pass,
++				 * and if this is nonblocking, we should return.. */
++			}
++
++			if (no_read > 200) {
++				set_errno(ECONNRESET);
++				return -1;
++			}
++		}
++
++		if (n < maxlen) {
++			m = recv(threaded->store.sock->fd, (void *) t, 1, flags);
++		}
++
++		if (errno != 0 && errno != ESPIPE && errno != EAGAIN) {
++			return -1;
++		}
++
++		set_errno(0);
++	}
++
++	if (n < maxlen) {
++		n++;
++		/* The only reasons it makes it to here is
++		 * if '\n' or '\r' are encountered. So, increase
++		 * the return by 1 to make up for the lack of the
++		 * '\n' or '\r' in the count (since read() takes
++		 * place at the end of the loop..) */
++	}
++
++	return n;
++}
++
++void pthreads_socket_read(zval *object, zend_long length, zend_long flags, zend_long type, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	zend_string *buf;
++	int bytes;
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (length < 1) {
++		RETURN_FALSE;
++	}
++
++	buf = zend_string_alloc(length, 0);
++
++	if (type == PTHREADS_NORMAL_READ) {
++		bytes = pthreads_normal_read(threaded, ZSTR_VAL(buf), length, flags);
++	} else {
++		bytes = recv(threaded->store.sock->fd, ZSTR_VAL(buf), length, flags);
++	}
++
++	if (bytes == -1) {
++		zend_string_free(buf);
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to read from socket", errno);
++
++		RETURN_FALSE;
++	} else if (!bytes) {
++		zend_string_release(buf);
++		RETURN_EMPTY_STRING();
++	}
++	buf = zend_string_truncate(buf, bytes, 0);
++
++	ZSTR_LEN(buf) = bytes;
++	ZSTR_VAL(buf)[ZSTR_LEN(buf)] = 0;
++
++	RETURN_NEW_STR(buf);
++}
++
++void pthreads_socket_write(zval *object, zend_string *buf, zend_long length, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++	int bytes;
++
++	if (!length) {
++		length = ZSTR_LEN(buf);
++	}
++
++#ifndef PHP_WIN32
++	bytes = write(threaded->store.sock->fd, ZSTR_VAL(buf), MIN(length, ZSTR_LEN(buf)));
++#else
++	bytes = send(threaded->store.sock->fd, ZSTR_VAL(buf), min(length, ZSTR_LEN(buf)), 0);
++#endif
++
++	if (bytes < 0) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to write to socket", errno);
++
++		RETURN_FALSE;
++	}
++	
++	ZVAL_LONG(return_value, bytes);
++}
++
++void pthreads_socket_send(zval *object, zend_string *buf, zend_long length, zend_long flags, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	int bytes;
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	bytes = send(threaded->store.sock->fd, ZSTR_VAL(buf), (ZSTR_LEN(buf) < length ? ZSTR_LEN(buf) : length), flags);
++
++	if (bytes == -1) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to write to socket", errno);
++
++		RETURN_FALSE;
++	}
++
++	ZVAL_LONG(return_value, bytes);
++}
++
++void pthreads_socket_close(zval *object, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (PTHREADS_CLOSE_SOCKET_INTERNAL(threaded->store.sock) != SUCCESS) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to close socket", errno);
++
++		RETURN_FALSE;
++	}
++
++	threaded->store.sock->fd = PTHREADS_INVALID_SOCKET;
++}
++
++void pthreads_socket_set_blocking(zval *object, zend_bool blocking, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (php_set_sock_blocking(threaded->store.sock->fd, blocking) != SUCCESS) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to set blocking mode", errno);
++
++		RETURN_FALSE;
++	}
++
++	threaded->store.sock->blocking = blocking;
++	RETURN_TRUE;
++}
++
++void pthreads_socket_get_sockaddr(zval *object, zend_long port, struct sockaddr *sa, zval *return_value) {
++	array_init(return_value);
++
++	switch (sa->sa_family) {
++#if HAVE_IPV6
++		case AF_INET6: {
++			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
++			char                 addr6[INET6_ADDRSTRLEN+1];
++
++			inet_ntop(AF_INET6, &sin6->sin6_addr, addr6, INET6_ADDRSTRLEN);
++
++			add_assoc_string(return_value, "host", addr6);
++			if (port) {
++				add_assoc_long(return_value, "port", htons(sin6->sin6_port));
++			}
++		} break;
++#endif
++		case AF_INET: {
++			struct sockaddr_in  *sin = (struct sockaddr_in *) sa;
++
++			add_assoc_string(return_value, "host", inet_ntoa(sin->sin_addr));
++			if (port) {
++				add_assoc_long(return_value, "port", htons(sin->sin_port));
++			}
++		} break;
++#ifndef _WIN32
++		case AF_UNIX: {
++			struct sockaddr_un  *s_un = (struct sockaddr_un *) sa;
++
++			add_assoc_string(return_value, "host", s_un->sun_path);
++		} break;
++#endif
++	}
++}
++
++void pthreads_socket_get_peer_name(zval *object, zend_bool port, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	php_sockaddr_storage  sa_storage;
++    struct sockaddr     *sa = (struct sockaddr *) &sa_storage;
++	socklen_t            salen = sizeof(php_sockaddr_storage);
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (getpeername(threaded->store.sock->fd, sa, &salen) < 0) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to retrieve peer name", errno);
++
++		RETURN_FALSE;
++	}
++
++	pthreads_socket_get_sockaddr(object, port, sa, return_value);
++}
++
++void pthreads_socket_get_sock_name(zval *object, zend_bool port, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	php_sockaddr_storage  sa_storage;
++    struct sockaddr     *sa = (struct sockaddr *) &sa_storage;
++	socklen_t            salen = sizeof(php_sockaddr_storage);
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if (getsockname(threaded->store.sock->fd, sa, &salen) < 0) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to retrieve socket name", errno);
++
++		RETURN_FALSE;
++	}
++
++	pthreads_socket_get_sockaddr(object, port, sa, return_value);
++}
++
++static inline int pthreads_sockets_to_fd_set(zval *sockets, fd_set *fds, php_socket_t *max_fd) /* {{{ */
++{
++	zval		*element;
++	int			num = 0;
++
++	if (Z_TYPE_P(sockets) != IS_ARRAY) {
++		return 0;
++	}
++
++	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(sockets), element) {
++		pthreads_object_t *threaded;
++
++		if (Z_TYPE_P(element) != IS_OBJECT ||
++			!instanceof_function(Z_OBJCE_P(element), pthreads_socket_entry)) {
++			continue;
++		}
++
++		threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(element));
++
++		PTHREADS_SOCKET_CHECK_EX(threaded->store.sock, 0);
++
++		PHP_SAFE_FD_SET(threaded->store.sock->fd, fds);
++
++		if (threaded->store.sock->fd > *max_fd) {
++			*max_fd = threaded->store.sock->fd;
++		}
++		num++;
++	} ZEND_HASH_FOREACH_END();
++
++	return num ? 1 : 0;
++}
++/* }}} */
++
++static int pthreads_sockets_from_fd_set(zval *sockets, fd_set *fds) /* {{{ */
++{
++	zval		*element;
++	zval		set;
++	int			num = 0;
++	zend_ulong  idx;
++	zend_string *key;
++
++	if (Z_TYPE_P(sockets) != IS_ARRAY) {
++		return 0;
++	}
++
++	array_init(&set);
++	ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sockets), idx, key, element) {
++		pthreads_object_t	*threaded;
++
++		if (Z_TYPE_P(element) != IS_OBJECT || 
++			!instanceof_function(Z_OBJCE_P(element), pthreads_socket_entry)) {
++			continue;
++		}
++
++		threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(element));
++
++		if (PHP_SAFE_FD_ISSET(threaded->store.sock->fd, fds)) {
++			if (key) {
++				if (!add_assoc_zval_ex(&set, ZSTR_VAL(key), ZSTR_LEN(key), element)) {
++					continue;
++				}
++			} else {
++				if (!add_index_zval(&set, idx, element)) {
++					continue;
++				}
++			}
++
++			Z_ADDREF_P(element);
++		}
++
++		num++;
++	} ZEND_HASH_FOREACH_END();
++
++	zval_ptr_dtor(sockets);
++
++	ZVAL_COPY_VALUE(sockets, &set);
++
++	return num ? 1 : 0;
++}
++/* }}} */
++
++void pthreads_socket_select(zval *read, zval *write, zval *except, zval *sec, uint32_t usec, zval *errorno, zval *return_value) {
++	fd_set rfds, wfds, efds;
++	php_socket_t mfd = 0;
++	int result = SUCCESS, sets = 0;
++	struct timeval  tv;
++	struct timeval *tv_p = NULL;
++
++	if(errorno != NULL) {
++		zval_ptr_dtor(errorno);
++		ZVAL_LONG(errorno, SUCCESS);
++	}
++
++	FD_ZERO(&rfds);
++	FD_ZERO(&wfds);
++	FD_ZERO(&efds);
++
++	if (read && Z_TYPE_P(read) == IS_ARRAY) {
++		sets += pthreads_sockets_to_fd_set(read, &rfds, &mfd);
++	}
++
++	if (write && Z_TYPE_P(write) == IS_ARRAY) {
++		sets += pthreads_sockets_to_fd_set(write, &wfds, &mfd);
++	}
++
++	if (except && Z_TYPE_P(except) == IS_ARRAY) {
++		sets += pthreads_sockets_to_fd_set(except, &efds, &mfd);
++	}
++
++	if (!sets) {
++		RETURN_FALSE;
++	}
++
++	PHP_SAFE_MAX_FD(mfd, 0);
++
++	/* If seconds is not set to null, build the timeval, else we wait indefinitely */
++	if (sec != NULL) {
++		zval tmp;
++
++		if (Z_TYPE_P(sec) != IS_LONG) {
++			tmp = *sec;
++			zval_copy_ctor(&tmp);
++			convert_to_long(&tmp);
++			sec = &tmp;
++		}
++
++		/* Solaris + BSD do not like microsecond values which are >= 1 sec */
++		if (usec > 999999) {
++			tv.tv_sec = Z_LVAL_P(sec) + (usec / 1000000);
++			tv.tv_usec = usec % 1000000;
++		} else {
++			tv.tv_sec = Z_LVAL_P(sec);
++			tv.tv_usec = usec;
++		}
++
++		tv_p = &tv;
++	}
++
++	result = select(mfd + 1, &rfds, &wfds, &efds, tv_p);
++
++	if (result == -1) {
++		int eno = php_socket_errno();
++
++		if(errorno != NULL) {
++			ZVAL_LONG(errorno, eno);
++		}
++		PTHREADS_HANDLE_SOCKET_ERROR(eno, "Error");
++
++		RETURN_FALSE;
++	}
++
++	if (read) {
++		pthreads_sockets_from_fd_set(read, &rfds);
++	}
++
++	if (write) {
++		pthreads_sockets_from_fd_set(write, &wfds);
++	}
++
++	if (except) {
++		pthreads_sockets_from_fd_set(except, &efds);
++	}
++
++	RETURN_LONG(result);
++}
++
++void pthreads_socket_free(pthreads_socket_t *socket, zend_bool closing) {
++	if (closing && PTHREADS_IS_VALID_SOCKET(socket)) {
++		PTHREADS_CLOSE_SOCKET_INTERNAL(socket);
++	}
++
++	efree(socket);
++}
++
++void pthreads_socket_recvfrom(zval *object, zval *buffer, zend_long len, zend_long flags, zval *name, zval *port, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	socklen_t			slen;
++	int					retval;
++	zend_string *recv_buf;
++
++	recv_buf = zend_string_alloc(len + 1, 0);
++
++	switch (threaded->store.sock->domain) {
++#ifndef _WIN32
++		case AF_UNIX: {
++			struct sockaddr_un	s_un;
++
++			slen = sizeof(s_un);
++			s_un.sun_family = AF_UNIX;
++			retval = recvfrom(threaded->store.sock->fd, ZSTR_VAL(recv_buf), len, flags, (struct sockaddr *)&s_un, (socklen_t *)&slen);
++
++			if (retval < 0) {
++				zend_string_free(recv_buf);
++				PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to recvfrom", errno);
++
++				RETURN_FALSE;
++			}
++			ZSTR_LEN(recv_buf) = retval;
++			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
++
++			zval_ptr_dtor(buffer);
++			zval_ptr_dtor(name);
++
++			ZVAL_NEW_STR(buffer, recv_buf);
++			ZVAL_STRING(name, s_un.sun_path);
++		} break;
++#endif
++		case AF_INET: {
++			struct sockaddr_in	sin;
++			char				*address;
++
++			slen = sizeof(sin);
++			memset(&sin, 0, slen);
++			sin.sin_family = AF_INET;
++
++			if (port == NULL) {
++				zend_string_free(recv_buf);
++				WRONG_PARAM_COUNT;
++			}
++
++			retval = recvfrom(threaded->store.sock->fd, ZSTR_VAL(recv_buf), len, flags, (struct sockaddr *)&sin, (socklen_t *)&slen);
++
++			if (retval < 0) {
++				zend_string_free(recv_buf);
++				PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to recvfrom", errno);
++
++				RETURN_FALSE;
++			}
++			ZSTR_LEN(recv_buf) = retval;
++			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
++
++			zval_ptr_dtor(buffer);
++			zval_ptr_dtor(name);
++			zval_ptr_dtor(port);
++
++			address = inet_ntoa(sin.sin_addr);
++
++			ZVAL_NEW_STR(buffer, recv_buf);
++			ZVAL_STRING(name, address ? address : "0.0.0.0");
++			ZVAL_LONG(port, ntohs(sin.sin_port));
++		} break;
++#if HAVE_IPV6
++		case AF_INET6: {
++			struct sockaddr_in6	sin6;
++			char				addr6[INET6_ADDRSTRLEN];
++
++			slen = sizeof(sin6);
++			memset(&sin6, 0, slen);
++			sin6.sin6_family = AF_INET6;
++
++			if (port == NULL) {
++				zend_string_free(recv_buf);
++				WRONG_PARAM_COUNT;
++			}
++
++			retval = recvfrom(threaded->store.sock->fd, ZSTR_VAL(recv_buf), len, flags, (struct sockaddr *)&sin6, (socklen_t *)&slen);
++
++			if (retval < 0) {
++				zend_string_free(recv_buf);
++				PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to recvfrom", errno);
++
++				RETURN_FALSE;
++			}
++			ZSTR_LEN(recv_buf) = retval;
++			ZSTR_VAL(recv_buf)[ZSTR_LEN(recv_buf)] = '\0';
++
++			zval_ptr_dtor(buffer);
++			zval_ptr_dtor(name);
++			zval_ptr_dtor(port);
++
++			memset(addr6, 0, INET6_ADDRSTRLEN);
++			inet_ntop(AF_INET6, &sin6.sin6_addr, addr6, INET6_ADDRSTRLEN);
++
++			ZVAL_NEW_STR(buffer, recv_buf);
++			ZVAL_STRING(name, addr6[0] ? addr6 : "::");
++			ZVAL_LONG(port, ntohs(sin6.sin6_port));
++		} break;
++#endif
++		default:
++			RETURN_FALSE;
++	}
++
++	RETURN_LONG(retval);
++}
++
++void pthreads_socket_sendto(zval *object, int argc, zend_string *buf, zend_long len, zend_long flags, zend_string *addr, zend_long port, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	int	retval;
++
++	switch (threaded->store.sock->domain) {
++#ifndef _WIN32
++		case AF_UNIX: {
++			struct sockaddr_un	s_un;
++
++			memset(&s_un, 0, sizeof(s_un));
++			s_un.sun_family = AF_UNIX;
++			snprintf(s_un.sun_path, 108, "%s", ZSTR_VAL(addr));
++
++			retval = sendto(threaded->store.sock->fd, ZSTR_VAL(buf), ((size_t)len > ZSTR_LEN(buf)) ? ZSTR_LEN(buf) : (size_t)len,	flags, (struct sockaddr *) &s_un, SUN_LEN(&s_un));
++		} break;
++#endif
++		case AF_INET: {
++			struct sockaddr_in	sin;
++
++			if (argc != 5) {
++				WRONG_PARAM_COUNT;
++			}
++
++			memset(&sin, 0, sizeof(sin));
++			sin.sin_family = AF_INET;
++			sin.sin_port = htons((unsigned short) port);
++
++			if (! pthreads_socket_set_inet_addr(threaded->store.sock, &sin, addr)) {
++				RETURN_FALSE;
++			}
++
++			retval = sendto(threaded->store.sock->fd, ZSTR_VAL(buf), ((size_t)len > ZSTR_LEN(buf)) ? ZSTR_LEN(buf) : (size_t)len, flags, (struct sockaddr *) &sin, sizeof(sin));
++		} break;
++#if HAVE_IPV6
++		case AF_INET6: {
++			struct sockaddr_in6	sin6;
++
++			if (argc != 5) {
++				WRONG_PARAM_COUNT;
++			}
++
++			memset(&sin6, 0, sizeof(sin6));
++			sin6.sin6_family = AF_INET6;
++			sin6.sin6_port = htons((unsigned short) port);
++
++			if (! pthreads_socket_set_inet6_addr(threaded->store.sock, &sin6, addr)) {
++				RETURN_FALSE;
++			}
++
++			retval = sendto(threaded->store.sock->fd, ZSTR_VAL(buf), ((size_t)len > ZSTR_LEN(buf)) ? ZSTR_LEN(buf) : (size_t)len, flags, (struct sockaddr *) &sin6, sizeof(sin6));
++		} break;
++#endif
++		default:
++			RETURN_FALSE;
++	}
++
++	if (retval == -1) {
++		PTHREADS_SOCKET_ERROR(threaded->store.sock, "Unable to write to socket", errno);
++		RETURN_FALSE;
++	}
++
++	RETURN_LONG(retval);
++}
++
++void pthreads_socket_get_last_error(zval *object, zend_bool clear, zval *return_value) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++
++	if(threaded->store.sock->error == SUCCESS) {
++		RETURN_FALSE;
++	}
++	RETVAL_LONG(threaded->store.sock->error);
++
++	if(clear) {
++		PTHREADS_CLEAR_SOCKET_ERROR(threaded->store.sock);
++	}
++}
++
++void pthreads_socket_strerror(zend_long error, zval *return_value) {
++	char *errstr;
++	errstr = php_socket_strerror(error, NULL, 0);
++
++	RETVAL_STRING(errstr);
++
++	efree(errstr);
++}
++
++void pthreads_socket_clear_error(zval *object) {
++	pthreads_object_t *threaded =
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	PTHREADS_SOCKET_CHECK(threaded->store.sock);
++	PTHREADS_CLEAR_SOCKET_ERROR(threaded->store.sock);
++}
++#endif
+diff --git a/ext/pthreads/src/socket.h b/ext/pthreads/src/socket.h
+new file mode 100644
+index 00000000..2fe6771f
+--- /dev/null
++++ b/ext/pthreads/src/socket.h
+@@ -0,0 +1,60 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_SOCKET_H
++#define HAVE_PTHREADS_SOCKET_H
++
++#ifdef HAVE_CONFIG_H
++#	include <config.h>
++#endif
++
++typedef struct _pthreads_socket_t {
++	php_socket_t fd;
++	zend_long domain;
++	zend_long type;
++	zend_long error;
++	zend_long protocol;
++	zend_bool blocking;
++} pthreads_socket_t;
++
++#define PTHREADS_NORMAL_READ 0x0001
++#define PTHREADS_BINARY_READ 0x0002
++
++pthreads_socket_t* pthreads_socket_alloc(void);
++void pthreads_socket_construct(zval *object, zend_long domain, zend_long type, zend_long protocol);
++void pthreads_socket_set_option(zval *object, zend_long level, zend_long name, zend_long value, zval *return_value);
++void pthreads_socket_get_option(zval *object, zend_long level, zend_long name, zval *return_value);
++void pthreads_socket_bind(zval *object, zend_string *address, zend_long port, zval *return_value);
++void pthreads_socket_listen(zval *object, zend_long backlog, zval *return_value);
++void pthreads_socket_accept(zval *object, zend_class_entry *ce, zval *return_value);
++void pthreads_socket_connect(zval *object, int argc, zend_string *address, zend_long port, zval *return_value);
++void pthreads_socket_read(zval *object, zend_long length, zend_long flags, zend_long type, zval *return_value);
++void pthreads_socket_write(zval *object, zend_string *buf, zend_long length, zval *return_value);
++void pthreads_socket_send(zval *object, zend_string *buf, zend_long length, zend_long flags, zval *return_value);
++void pthreads_socket_close(zval *object, zval *return_value);
++void pthreads_socket_set_blocking(zval *object, zend_bool blocking, zval *return_value);
++void pthreads_socket_get_peer_name(zval *object, zend_bool port, zval *return_value);
++void pthreads_socket_get_sock_name(zval *object, zend_bool port, zval *return_value);
++void pthreads_socket_select(zval *read, zval *write, zval *except, zval *sec, uint32_t usec, zval *errorno, zval *return_value);
++void pthreads_socket_free(pthreads_socket_t *socket, zend_bool closing);
++void pthreads_socket_recvfrom(zval *object, zval *buffer, zend_long len, zend_long flags, zval *name, zval *port, zval *return_value);
++void pthreads_socket_sendto(zval *object, int argc, zend_string *buf, zend_long len, zend_long flags, zend_string *addr, zend_long port, zval *return_value);
++void pthreads_socket_get_last_error(zval *object, zend_bool clear, zval *return_value);
++void pthreads_socket_strerror(zend_long error, zval *return_value);
++void pthreads_socket_clear_error(zval *object);
++
++#endif
+diff --git a/ext/pthreads/src/sockets/windows_common.h b/ext/pthreads/src/sockets/windows_common.h
+new file mode 100644
+index 00000000..a5ac4372
+--- /dev/null
++++ b/ext/pthreads/src/sockets/windows_common.h
+@@ -0,0 +1,117 @@
++/*
++   +----------------------------------------------------------------------+
++   | PHP Version 7                                                        |
++   +----------------------------------------------------------------------+
++   | Copyright (c) 1997-2018 The PHP Group                                |
++   +----------------------------------------------------------------------+
++   | 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.               |
++   +----------------------------------------------------------------------+
++ */
++#ifndef WINDOWS_COMMON_H
++#define WINDOWS_COMMON_H
++
++#include <Winsock2.h>
++#define NTDDI_XP NTDDI_WINXP /* bug in SDK */
++#include <IPHlpApi.h> /* conflicting definition of CMSG_DATA */
++#undef NTDDI_XP
++
++#define HAVE_IF_NAMETOINDEX 1
++
++#ifdef errno
++# undef errno
++#endif
++#define errno			WSAGetLastError()
++#define h_errno			WSAGetLastError()
++#define set_errno(a)	WSASetLastError(a)
++
++#ifdef ENETUNREACH /* errno.h probably included */
++# undef EWOULDBLOCK
++# undef EINPROGRESS
++# undef EALREADY
++# undef ENOTSOCK
++# undef EDESTADDRREQ
++# undef EMSGSIZE
++# undef EPROTOTYPE
++# undef ENOPROTOOPT
++# undef EPROTONOSUPPORT
++# undef ESOCKTNOSUPPORT
++# undef EOPNOTSUPP
++# undef EPFNOSUPPORT
++# undef EAFNOSUPPORT
++# undef EADDRINUSE
++# undef EADDRNOTAVAIL
++# undef ENETDOWN
++# undef ENETUNREACH
++# undef ENETRESET
++# undef ECONNABORTED
++# undef ECONNRESET
++# undef ENOBUFS
++# undef EISCONN
++# undef ENOTCONN
++# undef ESHUTDOWN
++# undef ETOOMANYREFS
++# undef ETIMEDOUT
++# undef ECONNREFUSED
++# undef ELOOP
++# undef ENAMETOOLONG
++# undef EHOSTDOWN
++# undef EHOSTUNREACH
++# undef ENOTEMPTY
++# undef EPROCLIM
++# undef EUSERS
++# undef EDQUOT
++# undef ESTALE
++# undef EREMOTE
++
++# undef EAGAIN
++#endif
++
++/* section disabled in WinSock2.h */
++#define EWOULDBLOCK             WSAEWOULDBLOCK
++#define EINPROGRESS             WSAEINPROGRESS
++#define EALREADY                WSAEALREADY
++#define ENOTSOCK                WSAENOTSOCK
++#define EDESTADDRREQ            WSAEDESTADDRREQ
++#define EMSGSIZE                WSAEMSGSIZE
++#define EPROTOTYPE              WSAEPROTOTYPE
++#define ENOPROTOOPT             WSAENOPROTOOPT
++#define EPROTONOSUPPORT         WSAEPROTONOSUPPORT
++#define ESOCKTNOSUPPORT         WSAESOCKTNOSUPPORT
++#define EOPNOTSUPP              WSAEOPNOTSUPP
++#define EPFNOSUPPORT            WSAEPFNOSUPPORT
++#define EAFNOSUPPORT            WSAEAFNOSUPPORT
++#define EADDRINUSE              WSAEADDRINUSE
++#define EADDRNOTAVAIL           WSAEADDRNOTAVAIL
++#define ENETDOWN                WSAENETDOWN
++#define ENETUNREACH             WSAENETUNREACH
++#define ENETRESET               WSAENETRESET
++#define ECONNABORTED            WSAECONNABORTED
++#define ECONNRESET              WSAECONNRESET
++#define ENOBUFS                 WSAENOBUFS
++#define EISCONN                 WSAEISCONN
++#define ENOTCONN                WSAENOTCONN
++#define ESHUTDOWN               WSAESHUTDOWN
++#define ETOOMANYREFS            WSAETOOMANYREFS
++#define ETIMEDOUT               WSAETIMEDOUT
++#define ECONNREFUSED            WSAECONNREFUSED
++#define ELOOP                   WSAELOOP
++#define ENAMETOOLONG            WSAENAMETOOLONG
++#define EHOSTDOWN               WSAEHOSTDOWN
++#define EHOSTUNREACH            WSAEHOSTUNREACH
++#define ENOTEMPTY               WSAENOTEMPTY
++#define EPROCLIM                WSAEPROCLIM
++#define EUSERS                  WSAEUSERS
++#define EDQUOT                  WSAEDQUOT
++#define ESTALE                  WSAESTALE
++#define EREMOTE                 WSAEREMOTE
++
++/* and an extra one */
++#define EAGAIN					WSAEWOULDBLOCK
++
++#endif
+diff --git a/ext/pthreads/src/stack.c b/ext/pthreads/src/stack.c
+new file mode 100644
+index 00000000..7438cc16
+--- /dev/null
++++ b/ext/pthreads/src/stack.c
+@@ -0,0 +1,291 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2015                                       |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_STACK
++#define HAVE_PTHREADS_STACK
++
++#ifdef HAVE_CONFIG_H
++#	include <config.h>
++#endif
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++#define PTHREADS_STACK_GARBAGE 2
++#define PTHREADS_STACK_FREE    1
++#define PTHREADS_STACK_NOTHING 0
++
++struct pthreads_stack_item_t {
++	struct pthreads_stack_item_t *next;
++	struct pthreads_stack_item_t *prev;
++	zval value;
++};
++
++struct pthreads_stack_t {
++	zend_long 				size;
++	pthreads_monitor_t   	*monitor;
++	struct pthreads_stack_t	*gc;
++	pthreads_stack_item_t	*head;
++	pthreads_stack_item_t	*tail;
++};
++
++pthreads_stack_t* pthreads_stack_alloc(pthreads_monitor_t *monitor) {
++	pthreads_stack_t *stack = 
++		(pthreads_stack_t*) ecalloc(1, sizeof(pthreads_stack_t));
++	
++	stack->monitor = monitor;
++	stack->gc = (pthreads_stack_t*) ecalloc(1, sizeof(pthreads_stack_t));
++
++	return stack;
++}
++
++zend_long pthreads_stack_size(pthreads_stack_t *stack) {
++	zend_long size = 0;
++
++	if (pthreads_monitor_lock(stack->monitor)) {
++		size = stack->size;
++		pthreads_monitor_unlock(stack->monitor);
++	}
++	return size;
++}
++
++void pthreads_stack_free(pthreads_stack_t *stack) {
++	pthreads_monitor_t *monitor = stack->monitor;
++
++	if (pthreads_monitor_lock(monitor)) {
++		pthreads_stack_item_t *item = stack->head;
++		while (item) {
++			pthreads_stack_item_t *r = item;
++			zval_ptr_dtor(&item->value);
++			item = r->next;
++			efree(r);
++		}
++
++		if (stack->gc) {
++			item = stack->gc->head;		
++			while (item) {
++				pthreads_stack_item_t *r = item;
++				zval_ptr_dtor(&item->value);
++				item = r->next;
++				efree(r);	
++			}
++		}
++
++		efree(stack->gc);
++		efree(stack);
++	
++		pthreads_monitor_unlock(monitor);
++	}
++}
++
++static inline void pthreads_stack_add_item(pthreads_stack_t *stack, pthreads_stack_item_t *item) {
++	if (!stack->tail) {
++		stack->tail = item;
++		stack->head = item;
++	} else {
++		stack->tail->next = item;
++		item->prev = stack->tail;
++		stack->tail = item;
++	}
++	item->next = NULL;
++	stack->size++;
++}
++
++zend_long pthreads_stack_add(pthreads_stack_t *stack, zval *value) {
++	zend_long size = 0;
++	pthreads_stack_item_t *item = 
++			(pthreads_stack_item_t*) ecalloc(1, sizeof(pthreads_stack_item_t));
++
++	ZVAL_COPY(&item->value, value);
++
++	if (pthreads_monitor_lock(stack->monitor)) {
++		size = stack->size;
++		pthreads_stack_add_item(stack, item);
++		if (!size) {
++			pthreads_monitor_notify(stack->monitor);
++		}
++		size = stack->size;
++		pthreads_monitor_unlock(stack->monitor);
++	} else {
++		zval_ptr_dtor(&item->value);
++		efree(item);
++		size = -1;
++	}
++
++	return size;
++}
++
++static inline zend_long pthreads_stack_remove(pthreads_stack_t *stack, pthreads_stack_item_t *item, zval *value, zend_bool garbage) {
++	if (!item) {
++		value = NULL;
++		return 0;
++	}
++
++	if (stack->head == item && stack->tail == item) {
++		stack->head = NULL;
++		stack->tail = NULL;
++	} else if (stack->head == item) {
++		stack->head = item->next;
++		stack->head->prev = NULL;
++	} else if (stack->tail == item) {
++		stack->tail = item->prev;
++		stack->tail->next = NULL;
++	} else {
++		pthreads_stack_item_t *items[2] = 
++			{item->next, item->prev};
++		
++		items[0]->prev = items[1];
++		items[1]->next = items[0];
++	}
++
++	stack->size--;
++
++	if (value) {
++		memcpy(value, &item->value, sizeof(zval));
++	}
++	
++	switch (garbage) {
++		case PTHREADS_STACK_GARBAGE:
++			pthreads_stack_add_item(stack->gc, item);
++		break;
++
++		case PTHREADS_STACK_FREE:
++			efree(item);
++		break;
++	}
++
++	return stack->size;
++}
++
++zend_long pthreads_stack_del(pthreads_stack_t *stack, zval *value) {
++	zend_long size = 0;
++	
++	if (pthreads_monitor_lock(stack->monitor)) {
++		size = pthreads_stack_remove(
++			stack, stack->head, value, PTHREADS_STACK_FREE);
++		pthreads_monitor_unlock(stack->monitor);
++	}
++
++	return size;
++}
++
++zend_long pthreads_stack_collect(zend_object *std, pthreads_stack_t *stack, pthreads_call_t *call, pthreads_stack_running_function_t running, pthreads_stack_collect_function_t collect) {
++	zend_long size = 0, offset = 0;
++
++	if (pthreads_monitor_lock(stack->monitor)) {
++		pthreads_stack_item_t *item;
++
++		if (stack->gc) {
++			item = stack->gc->head;
++			while (item) {
++				if (running(std, &item->value)) {
++					offset++;
++					/* we break out of gc if the worker is executing something on the stack */
++					/* this means gc is only performed while the worker is idle */
++					/* this means we avoid contention for locks on objects the programmer thinks are executing */
++					break;
++				}
++
++				if (collect(call, &item->value)) {
++					pthreads_stack_item_t *garbage = item;
++
++					pthreads_stack_remove(
++						stack->gc, garbage, NULL, PTHREADS_STACK_NOTHING);
++					item = garbage->next;
++					zval_ptr_dtor(&garbage->value);
++					efree(garbage);
++					continue;
++				}
++
++				item = item->next;
++			}
++
++			size = (stack->size + stack->gc->size) + -offset;
++		}
++
++		pthreads_monitor_unlock(stack->monitor);
++	}
++
++	return size;
++}
++
++pthreads_monitor_state_t pthreads_stack_next(pthreads_stack_t *stack, zval *value, zend_object **running) {
++	pthreads_monitor_state_t state = PTHREADS_MONITOR_RUNNING;
++	if (pthreads_monitor_lock(stack->monitor)) {
++#define SET_RUNNING_TO(t) *running = t
++		do {
++			if (!stack->head) {
++				if (pthreads_monitor_check(stack->monitor, PTHREADS_MONITOR_JOINED)) {
++					state = PTHREADS_MONITOR_JOINED;
++					SET_RUNNING_TO(NULL);
++					break;
++				}
++				
++				SET_RUNNING_TO(NULL);
++				pthreads_monitor_wait(stack->monitor, 0);
++			} else {
++				pthreads_stack_remove(stack, stack->head, value, PTHREADS_STACK_GARBAGE);
++				SET_RUNNING_TO(Z_OBJ_P(value));
++				break;
++			}
++		} while (state != PTHREADS_MONITOR_JOINED);
++#undef SET_RUNNING_TO
++		pthreads_monitor_unlock(stack->monitor);
++	}
++
++	return state;
++}
++
++void pthreads_stack_tohash(pthreads_stack_t *stack, HashTable *hash) {
++	zval stacked;
++	zval waiting;
++	zval gc;
++
++	array_init(&stacked);
++	array_init(&waiting);	
++	array_init(&gc);
++
++	zend_hash_str_add(Z_ARRVAL(stacked), ":stacked:", sizeof(":stacked:")-1, &waiting);
++	zend_hash_str_add(Z_ARRVAL(stacked), ":gc:", sizeof(":gc:")-1, &gc);
++
++	if (pthreads_monitor_lock(stack->monitor)) {
++		pthreads_stack_item_t *item = stack->head;
++		
++		while (item) {
++			if (add_next_index_zval(
++					&waiting, &item->value)) {
++				Z_ADDREF(item->value);
++			}
++			item = item->next;
++		}
++
++		item = stack->gc->head;
++		while (item) {
++			if (add_next_index_zval(
++					&gc, &item->value)) {
++				Z_ADDREF(item->value);
++			}
++			item = item->next;
++		}
++		pthreads_monitor_unlock(stack->monitor);
++	}
++
++	zend_hash_str_add(hash, ":stack:", sizeof(":stack:")-1, &stacked);
++}
++#endif
++
+diff --git a/ext/pthreads/src/stack.h b/ext/pthreads/src/stack.h
+new file mode 100644
+index 00000000..f960f480
+--- /dev/null
++++ b/ext/pthreads/src/stack.h
+@@ -0,0 +1,40 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2015                                       |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_STACK_H
++#define HAVE_PTHREADS_STACK_H
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++typedef struct pthreads_stack_t pthreads_stack_t;
++typedef struct pthreads_stack_item_t pthreads_stack_item_t;
++typedef zend_bool (*pthreads_stack_running_function_t) (zend_object *std, zval *value);
++typedef zend_bool (*pthreads_stack_collect_function_t) (pthreads_call_t *call, zval *value);
++
++pthreads_stack_t* pthreads_stack_alloc(pthreads_monitor_t *monitor);
++zend_long pthreads_stack_size(pthreads_stack_t *stack);
++void pthreads_stack_free(pthreads_stack_t *stack);
++zend_long pthreads_stack_add(pthreads_stack_t *stack, zval *value);
++zend_long pthreads_stack_del(pthreads_stack_t *stack, zval *value);
++zend_long pthreads_stack_collect(zend_object *std, pthreads_stack_t *stack, pthreads_call_t *call, pthreads_stack_running_function_t running, pthreads_stack_collect_function_t collect);
++pthreads_monitor_state_t pthreads_stack_next(pthreads_stack_t *stack, zval *value, zend_object **running);
++
++void pthreads_stack_tohash(pthreads_stack_t *stack, HashTable *hash);
++#endif
++
+diff --git a/ext/pthreads/src/store.c b/ext/pthreads/src/store.c
+new file mode 100644
+index 00000000..0c9bde25
+--- /dev/null
++++ b/ext/pthreads/src/store.c
+@@ -0,0 +1,1089 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_STORE
++#define HAVE_PTHREADS_STORE
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++#ifndef HAVE_PTHREADS_GLOBALS_H
++#	include <src/globals.h>
++#endif
++
++#ifndef HAVE_PTHREADS_OBJECT_H
++#	include <src/object.h>
++#endif
++
++#ifndef HAVE_PTHREADS_RESOURCES_H
++#	include <src/resources.h>
++#endif
++
++#ifndef HAVE_PTHREADS_COPY_H
++#	include <src/copy.h>
++#endif
++
++#ifndef HAVE_PTHREADS_STORE_H
++#	include <src/store.h>
++#endif
++
++
++#define PTHREADS_STORAGE_EMPTY {0, 0, 0, 0, NULL}
++
++/* {{{ */
++static int pthreads_store_tostring(zval *pzval, char **pstring, size_t *slength, zend_bool complex);
++static int pthreads_store_tozval(zval *pzval, char *pstring, size_t slength);
++/* }}} */
++
++/* {{{ */
++static inline void pthreads_store_storage_table_dtor (zval *element) {
++	pthreads_store_storage_dtor(Z_PTR_P(element));
++} /* }}} */
++
++/* {{{ */
++pthreads_store_t* pthreads_store_alloc() {
++	pthreads_store_t *store = (pthreads_store_t*) calloc(1, sizeof(pthreads_store_t));
++
++	if (store) {
++		zend_hash_init(
++			store, 8, NULL, 
++			(dtor_func_t) pthreads_store_storage_table_dtor, 1);
++	}
++
++	return store;
++} /* }}} */
++
++void pthreads_store_sync(zval *object) { /* {{{ */
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	zend_ulong idx;
++	zend_string *name;
++
++	rebuild_object_properties(&threaded->std);
++
++	ZEND_HASH_FOREACH_KEY(threaded->std.properties, idx, name) {
++		if (!name) {
++			if (!zend_hash_index_exists(threaded->store.props, idx))
++				zend_hash_index_del(threaded->std.properties, idx);
++		} else {
++			if (!zend_hash_exists(threaded->store.props, name))
++				zend_hash_del(threaded->std.properties, name);
++		}
++	} ZEND_HASH_FOREACH_END();
++} /* }}} */
++
++static inline zend_bool pthreads_store_coerce(HashTable *table, zval *key, zval *member) {
++	zend_ulong hval;
++
++	if (!key || Z_TYPE_P(key) == IS_NULL) {
++		ZVAL_LONG(member, zend_hash_next_free_element(table));
++		return 0;
++	}
++
++	switch (Z_TYPE_P(key)) {
++		case IS_STRING:
++			if (ZEND_HANDLE_NUMERIC(Z_STR_P(key), hval)) {
++				ZVAL_LONG(member, hval);
++				return 0;
++			}
++		case IS_LONG:
++			ZVAL_ZVAL(member, key, 0, 0);
++			return 0;
++
++		default:
++			ZVAL_STR(member, zval_get_string(key));
++			return 1;
++	}
++}
++
++/* {{{ */
++static inline zend_bool pthreads_store_is_immutable(zval *object, zval *key) {	
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	pthreads_storage *storage;
++
++	if (IS_PTHREADS_VOLATILE(object)) {
++		return 0;
++	}
++
++	if (Z_TYPE_P(key) == IS_LONG) {
++		storage = zend_hash_index_find_ptr(threaded->store.props, Z_LVAL_P(key));
++	} else storage = zend_hash_find_ptr(threaded->store.props, Z_STR_P(key));
++
++	if ((storage) && (storage->type == IS_PTHREADS)) {
++		if (Z_TYPE_P(key) == IS_LONG) {
++			zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++			"Threaded members previously set to Threaded objects are immutable, cannot overwrite %ld",
++			Z_LVAL_P(key));	
++		} else zend_throw_exception_ex(spl_ce_RuntimeException, 0,
++			"Threaded members previously set to Threaded objects are immutable, cannot overwrite %s",
++			Z_STRVAL_P(key));
++		return 1;
++	}
++
++	return 0;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_delete(zval *object, zval *key) {
++	int result = FAILURE;
++	zval member, *property = NULL;
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	zend_bool coerced = pthreads_store_coerce(threaded->store.props, key, &member);	
++
++	rebuild_object_properties(&threaded->std);
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		if (!pthreads_store_is_immutable(object, &member)) {
++			if (Z_TYPE(member) == IS_LONG) {
++				result = zend_hash_index_del(threaded->store.props, Z_LVAL(member));
++			} else result = zend_hash_del(threaded->store.props, Z_STR(member));
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++	} else result = FAILURE;
++
++	if (result == SUCCESS) {
++		if (Z_TYPE(member) == IS_LONG) {
++			zend_hash_index_del(threaded->std.properties, Z_LVAL(member));
++		} else zend_hash_del(threaded->std.properties, Z_STR(member));
++	}
++
++	if (coerced)
++		zval_ptr_dtor(&member);
++
++	return result;
++}
++/* }}} */
++
++/* {{{ */
++zend_bool pthreads_store_isset(zval *object, zval *key, int has_set_exists) {
++	zend_bool isset = 0;
++	zval member;
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	zend_bool coerced = pthreads_store_coerce(threaded->store.props, key, &member);
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		pthreads_storage *storage;
++
++		if (Z_TYPE(member) == IS_LONG) {
++			storage = zend_hash_index_find_ptr(threaded->store.props, Z_LVAL(member));
++		} else storage = zend_hash_find_ptr(threaded->store.props, Z_STR(member));
++
++		isset = storage != NULL;
++
++		if (has_set_exists && storage) {
++		    switch (storage->type) {
++				case IS_LONG:
++				case IS_TRUE:
++				case IS_FALSE:
++					if (storage->simple.lval == 0)
++					isset = 0;
++				break;
++		
++				case IS_ARRAY:
++					if (storage->exists == 0)
++					isset = 0;
++				break;
++		
++				case IS_STRING: switch (storage->length) {
++					case 0:
++					isset = 0;
++					break;
++					
++					case 1:
++					if (memcmp(storage->data, "0", 1) == SUCCESS)
++						isset = 0;
++					break;
++				} break;
++		
++				case IS_DOUBLE:
++					if (storage->simple.dval == 0.0)
++					isset = 0;
++				break;
++					
++				case IS_NULL:
++					isset = 0;
++				break;
++		    }
++		} else if (isset) {
++			switch (storage->type) {
++			    case IS_NULL:
++				isset = 0;
++			    break;
++			}
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++
++	if (coerced) 
++		zval_ptr_dtor(&member);
++
++	return isset;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_read(zval *object, zval *key, int type, zval *read) {
++	int result = FAILURE;
++	zval member, *property = NULL;
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	zend_bool coerced = pthreads_store_coerce(threaded->store.props, key, &member);
++
++	rebuild_object_properties(&threaded->std);
++
++	if (Z_TYPE(member) == IS_LONG) {
++		property = zend_hash_index_find(threaded->std.properties, Z_LVAL(member));
++	} else property = zend_hash_find(threaded->std.properties, Z_STR(member));
++
++	if (property && IS_PTHREADS_VOLATILE(object)) {
++		if (pthreads_monitor_lock(threaded->monitor)) {
++			pthreads_storage *storage;
++
++			if (Z_TYPE(member) == IS_LONG) {
++				storage = zend_hash_index_find_ptr(threaded->store.props, Z_LVAL(member));
++			} else storage = zend_hash_find_ptr(threaded->store.props, Z_STR(member));
++
++			if (storage && storage->type == IS_PTHREADS) {
++				pthreads_object_t* threadedStorage = PTHREADS_FETCH_FROM(storage->data);
++				pthreads_object_t *threadedProperty = PTHREADS_FETCH_FROM(Z_OBJ_P(property));
++
++				if (threadedStorage->monitor != threadedProperty->monitor) {
++					property = NULL;
++				}
++			} else {
++				property = NULL;
++			}
++			pthreads_monitor_unlock(threaded->monitor);
++		}
++	}
++
++	if (property && IS_PTHREADS_OBJECT(property)) {
++		ZVAL_COPY(read, property);
++		if (coerced) {
++			zval_ptr_dtor(&member);
++		}
++		return SUCCESS;
++	}
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		pthreads_storage *storage;
++
++		/* synchronize property stores */
++		pthreads_store_sync(object);
++
++		if (Z_TYPE(member) == IS_LONG) {
++			storage = zend_hash_index_find_ptr(threaded->store.props, Z_LVAL(member));
++		} else storage = zend_hash_find_ptr(threaded->store.props, Z_STR(member));
++		
++		if (storage) {
++			result = pthreads_store_convert(storage, read);
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++
++	if (result != SUCCESS) {
++		ZVAL_NULL(read);
++	} else {
++		if (IS_PTHREADS_OBJECT(read)) {
++			rebuild_object_properties(&threaded->std);
++			if (Z_TYPE(member) == IS_LONG) {
++				zend_hash_index_update(threaded->std.properties, Z_LVAL(member), read);
++			} else zend_hash_update(threaded->std.properties, Z_STR(member), read);
++			Z_ADDREF_P(read);
++		}
++	}
++
++	if (coerced)
++		zval_ptr_dtor(&member);
++
++	return SUCCESS;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_write(zval *object, zval *key, zval *write) {
++	int result = FAILURE;
++	pthreads_storage *storage;
++	zval vol, member, *property = NULL, *read = NULL;
++	pthreads_object_t *threaded = 
++		PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	zend_bool coerced = 0;
++
++	if (Z_TYPE_P(write) == IS_ARRAY) {
++		if (!pthreads_check_opline_ex(EG(current_execute_data), -1, ZEND_CAST, IS_ARRAY) &&
++			!pthreads_check_opline_ex(EG(current_execute_data), -2, ZEND_CAST, IS_ARRAY)) {
++			/* coerce arrays into volatile objects unless explicitly cast as array */
++			object_init_ex(
++				&vol, pthreads_volatile_entry);
++			pthreads_store_merge(&vol, write, 1);
++			/* this will be addref'd when caching the object */
++			Z_SET_REFCOUNT(vol, 0);
++			write = &vol;
++		}
++	}
++
++	if (Z_TYPE_P(write) == IS_OBJECT) {
++		/* when we copy out in another context, we want properties table
++			to be initialized */
++		rebuild_object_properties(Z_OBJ_P(write));
++	}
++
++	storage = pthreads_store_create(write, 1);
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		coerced = pthreads_store_coerce(threaded->store.props, key, &member);
++		if (!pthreads_store_is_immutable(object, &member)) {
++			if (Z_TYPE(member) == IS_LONG) {
++				if (zend_hash_index_update_ptr(threaded->store.props, Z_LVAL(member), storage))
++					result = SUCCESS;
++			} else {
++				/* we can't use global strings here */
++				zend_string *keyed = zend_string_dup(Z_STR(member), 1);
++
++				if (zend_hash_update_ptr(threaded->store.props, keyed, storage)) {
++					result = SUCCESS;
++				}
++				zend_string_release(keyed);
++			}
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++
++	if (result != SUCCESS) {
++		pthreads_store_storage_dtor(storage);
++	} else {
++		if (IS_PTHREADS_OBJECT(write) || IS_PTHREADS_CLOSURE(write)) {
++			/*
++				This could be a volatile object, but, we don't want to break
++				normal refcounting, we'll read the reference only at volatile objects
++			*/
++			rebuild_object_properties(&threaded->std);
++			
++			if(IS_PTHREADS_VOLATILE(object)) {
++				pthreads_store_sync(object);
++			}
++
++			if (Z_TYPE(member) == IS_LONG) {
++				zend_hash_index_update(threaded->std.properties, Z_LVAL(member), write);
++			} else {
++				zend_string *keyed = zend_string_dup(Z_STR(member), 0);
++				if (zend_hash_update(
++					threaded->std.properties, keyed, write)) {
++					result = SUCCESS;
++				}
++				zend_string_release(keyed);
++			}
++			Z_ADDREF_P(write);
++		}
++	}
++	
++	if (coerced)
++		zval_ptr_dtor(&member);
++	
++	return result;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_separate(zval * pzval, zval *separated, zend_bool complex) {
++	int result = FAILURE;
++	pthreads_storage *storage;
++	
++	if (Z_TYPE_P(pzval) != IS_NULL) {
++		storage = pthreads_store_create(pzval, complex);
++		result = pthreads_store_convert(storage, separated);
++		pthreads_store_storage_dtor(storage);
++	} else ZVAL_NULL(separated);
++
++	return result;
++} /* }}} */
++
++/* {{{ */
++void pthreads_store_separate_zval(zval *zv) {
++	pthreads_storage *storage;
++	zval in = *zv;
++
++	if (Z_TYPE(in) != IS_NULL) {
++        	storage = pthreads_store_create(&in, 1);
++		pthreads_store_convert(storage, zv);
++	    pthreads_store_storage_dtor(storage);
++	}
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_count(zval *object, zend_long *count) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		(*count) = zend_hash_num_elements(threaded->store.props);
++		pthreads_monitor_unlock(threaded->monitor);
++	} else (*count) = 0L;
++   
++	return SUCCESS;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_shift(zval *object, zval *member) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		zval key;
++		HashPosition position;
++		pthreads_storage *storage;
++
++		zend_hash_internal_pointer_reset_ex(threaded->store.props, &position);
++		if ((storage = zend_hash_get_current_data_ptr_ex(threaded->store.props, &position))) {
++			zend_hash_get_current_key_zval_ex(threaded->store.props, &key, &position);
++			if (!pthreads_store_is_immutable(object, &key)) {
++				pthreads_store_convert(storage, member);
++				if (Z_TYPE(key) == IS_LONG) {
++					zend_hash_index_del(threaded->store.props, Z_LVAL(key));
++					zend_hash_index_del(threaded->std.properties, Z_LVAL(key));
++				} else {
++					zend_hash_del(threaded->store.props, Z_STR(key));
++					zend_hash_del(threaded->std.properties, Z_STR(key));
++				}
++			}
++		} else ZVAL_NULL(member);
++		pthreads_monitor_unlock(threaded->monitor);
++
++		return SUCCESS;
++	}
++	
++    return FAILURE;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_chunk(zval *object, zend_long size, zend_bool preserve, zval *chunk) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		HashPosition position;
++		pthreads_storage *storage;
++
++		array_init(chunk);
++		zend_hash_internal_pointer_reset_ex(threaded->store.props, &position);
++		while((zend_hash_num_elements(Z_ARRVAL_P(chunk)) < size) &&
++			(storage = zend_hash_get_current_data_ptr_ex(threaded->store.props, &position))) {
++			zval key, zv;
++
++			zend_hash_get_current_key_zval_ex(threaded->store.props, &key, &position);		
++
++			if (!pthreads_store_is_immutable(object, &key)) {
++				pthreads_store_convert(storage, &zv);
++				if (Z_TYPE(key) == IS_LONG) {
++					zend_hash_index_update(
++						Z_ARRVAL_P(chunk), Z_LVAL(key), &zv);
++					zend_hash_index_del(threaded->store.props, Z_LVAL(key));
++					zend_hash_index_del(threaded->std.properties, Z_LVAL(key));
++				} else {
++					zend_hash_update(
++						Z_ARRVAL_P(chunk), Z_STR(key), &zv);
++					zend_hash_del(threaded->store.props, Z_STR(key));
++					zend_hash_del(threaded->std.properties, Z_STR(key));
++				}
++			} else break;
++
++			zend_hash_internal_pointer_reset_ex(threaded->store.props, &position);
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++
++		return SUCCESS;
++	}
++
++    return FAILURE;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_pop(zval *object, zval *member) {
++	pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++   
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		zval key;
++		HashPosition position;
++		pthreads_storage *storage;
++
++		zend_hash_internal_pointer_end_ex(threaded->store.props, &position);
++		if ((storage = zend_hash_get_current_data_ptr_ex(threaded->store.props, &position))) {
++			zend_hash_get_current_key_zval_ex(threaded->store.props, &key, &position);
++
++			if (!pthreads_store_is_immutable(object, &key)) {
++				pthreads_store_convert(storage, member);
++
++				if (Z_TYPE(key) == IS_LONG) {
++					zend_hash_index_del(
++						threaded->store.props, Z_LVAL(key));
++					zend_hash_index_del(
++						threaded->std.properties, Z_LVAL(key));	
++				} else {
++					zend_hash_del(
++						threaded->store.props, Z_STR(key));	
++					zend_hash_del(
++						threaded->std.properties, Z_STR(key));			
++				}
++			}
++		} else ZVAL_NULL(member);
++
++		pthreads_monitor_unlock(threaded->monitor);
++
++		return SUCCESS;
++	}
++   
++   return FAILURE;
++} /* }}} */
++
++/* {{{ */
++void pthreads_store_tohash(zval *object, HashTable *hash) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	rebuild_object_properties(&threaded->std);
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		zend_string *name = NULL;
++		zend_ulong idx;
++		pthreads_storage *storage;
++
++		pthreads_store_sync(object);
++
++		ZEND_HASH_FOREACH_KEY_PTR(threaded->store.props, idx, name, storage) {
++			zval pzval;
++			zend_string *rename;
++
++			/* don't overwrite pthreads objects for non volatile objects */
++			if (!IS_PTHREADS_VOLATILE(object) && storage->type == IS_PTHREADS) {
++				if (!name) {
++					if (zend_hash_index_exists(hash, idx))
++						continue;
++				} else {
++					if (zend_hash_exists(hash, name))
++						continue;
++				}
++			}
++
++			if (pthreads_store_convert(storage, &pzval)!=SUCCESS) {
++				continue;
++			}
++
++			if (!name) {
++				if (!zend_hash_index_update(hash, idx, &pzval)) {
++					zval_ptr_dtor(&pzval);
++				}
++			} else {
++				rename = zend_string_init(name->val, name->len, 0);
++				if (!zend_hash_update(hash, rename, &pzval))
++					zval_ptr_dtor(&pzval);
++				zend_string_release(rename);
++			}
++		} ZEND_HASH_FOREACH_END();
++
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++} /* }}} */
++
++/* {{{ */
++void pthreads_store_free(pthreads_store_t *store){
++	zend_hash_destroy(store);
++	free(store);
++} /* }}} */
++
++/* {{{ */
++pthreads_storage* pthreads_store_create(zval *unstore, zend_bool complex){
++	pthreads_storage *storage = NULL;
++
++	if (Z_TYPE_P(unstore) == IS_INDIRECT)
++		return pthreads_store_create(Z_INDIRECT_P(unstore), complex);
++	if (Z_TYPE_P(unstore) == IS_REFERENCE)
++		return pthreads_store_create(&Z_REF_P(unstore)->val, complex);
++
++	storage = (pthreads_storage*) calloc(1, sizeof(pthreads_storage));
++	
++	switch((storage->type = Z_TYPE_P(unstore))){
++		case IS_NULL: /* do nothing */ break;
++		case IS_TRUE: storage->simple.lval = 1; break;
++		case IS_FALSE: storage->simple.lval = 0; break;
++		case IS_DOUBLE: storage->simple.dval = Z_DVAL_P(unstore); break;
++		case IS_LONG: storage->simple.lval = Z_LVAL_P(unstore); break;
++
++		case IS_STRING: if ((storage->length = Z_STRLEN_P(unstore))) {
++			storage->data = 
++				(char*) malloc(storage->length+1);
++			memcpy(storage->data, Z_STRVAL_P(unstore), storage->length);
++			((char *)storage->data)[storage->length] = 0;
++		} break;
++
++		case IS_RESOURCE: {
++			if (complex) {
++				pthreads_resource resource = malloc(sizeof(*resource));
++				if (resource) {
++					resource->original = Z_RES_P(unstore);
++					resource->ls = TSRMLS_CACHE;
++					
++					storage->data = resource;
++					Z_ADDREF_P(unstore);
++				}
++			} else storage->type = IS_NULL;
++		} break;
++		
++		case IS_OBJECT:
++			if (instanceof_function(Z_OBJCE_P(unstore), zend_ce_closure)) {
++				const zend_function *def = 
++				    zend_get_closure_method_def(unstore);
++				storage->type = IS_CLOSURE;
++				storage->data = 
++				    (zend_function*) malloc(sizeof(zend_op_array));
++				memcpy(storage->data, def, sizeof(zend_op_array));
++				break;
++			}
++
++			if (instanceof_function(Z_OBJCE_P(unstore), pthreads_threaded_entry)) {
++				storage->type = IS_PTHREADS;
++				storage->data = Z_OBJ_P(unstore);
++				break;
++			}
++
++			if (!complex) {
++				storage->type = IS_NULL;
++				break;
++			}
++
++		/* break intentionally omitted */
++		case IS_ARRAY: if (pthreads_store_tostring(unstore, (char**) &storage->data, &storage->length, complex)==SUCCESS) {
++			if (Z_TYPE_P(unstore) == IS_ARRAY)
++				storage->exists = zend_hash_num_elements(Z_ARRVAL_P(unstore));
++		} break;
++
++	}
++	return storage;
++}
++/* }}} */
++
++/* {{{ */
++int pthreads_store_convert(pthreads_storage *storage, zval *pzval){
++	int result = SUCCESS;
++
++	switch(storage->type) {
++		case IS_NULL: ZVAL_NULL(pzval); break;
++
++		case IS_STRING:
++			if (storage->data && storage->length) {
++				ZVAL_STRINGL(pzval, (char*)storage->data, storage->length);
++			} else ZVAL_EMPTY_STRING(pzval);
++		break;
++		
++		case IS_FALSE:
++		case IS_TRUE: ZVAL_BOOL(pzval, storage->simple.lval); break;
++
++		case IS_LONG: ZVAL_LONG(pzval, storage->simple.lval); break;
++		case IS_DOUBLE: ZVAL_DOUBLE(pzval, storage->simple.dval); break;
++		case IS_RESOURCE: {	
++			pthreads_resource stored = (pthreads_resource) storage->data;
++
++			if (stored->ls != TSRMLS_CACHE) {
++				zval *search = NULL;
++				zend_ulong index = 0;
++				zend_string *name = NULL;
++				zend_resource *resource, *found = NULL;
++
++				ZEND_HASH_FOREACH_KEY_VAL(&EG(regular_list), index, name, search) {
++					resource = Z_RES_P(search);
++					if (resource->ptr == stored->original->ptr) {
++						found = resource;
++						break;
++					}
++				} ZEND_HASH_FOREACH_END();
++
++				if (!found) {
++					ZVAL_RES(pzval, stored->original);
++					if (zend_hash_next_index_insert(&EG(regular_list), pzval)) {
++					    pthreads_resources_keep(stored);
++					} else ZVAL_NULL(pzval);
++					Z_ADDREF_P(pzval);
++				} else ZVAL_COPY(pzval, search);
++			} else {
++				ZVAL_RES(pzval, stored->original);
++				Z_ADDREF_P(pzval);
++			}
++		} break;
++		
++		case IS_CLOSURE: {
++			char *name;
++			size_t name_len;
++			zend_string *zname;
++			zend_function *closure = pthreads_copy_function((zend_function*) storage->data);
++
++			zend_create_closure(pzval, closure, zend_get_executed_scope(), closure->common.scope, NULL);
++
++			name_len = spprintf(&name, 0, "Closure@%p", zend_get_closure_method_def(pzval));
++			zname = zend_string_init(name, name_len, 0);
++
++			if (!zend_hash_update_ptr(EG(function_table), zname, closure)) {
++				result = FAILURE;
++				zval_dtor(pzval);
++			} else result = SUCCESS;
++			efree(name);
++			zend_string_release(zname);
++		} break;
++
++		case IS_PTHREADS: {
++			pthreads_object_t* threaded = PTHREADS_FETCH_FROM(storage->data);
++
++			if (pthreads_check_opline_ex(EG(current_execute_data), 1, ZEND_CAST, IS_OBJECT)) {
++				ZVAL_OBJ(pzval, &threaded->std);
++				Z_ADDREF_P(pzval);
++				break;
++			}
++
++			if (!pthreads_globals_object_connect((zend_ulong)threaded, NULL, pzval)) {
++				zend_throw_exception_ex(
++					spl_ce_RuntimeException, 0,
++					"pthreads detected an attempt to connect to an object which has already been destroyed");
++				result = FAILURE;
++			}
++		} break;
++
++		case IS_OBJECT:
++		case IS_ARRAY:
++			result = pthreads_store_tozval(pzval, (char*) storage->data, storage->length);
++		break;
++
++		default: ZVAL_NULL(pzval);
++	}
++
++	if (result == FAILURE) {
++	    ZVAL_NULL(pzval);
++	}
++
++	return result;
++}
++/* }}} */
++
++/* {{{ */
++static inline int pthreads_store_remove_complex(zval *pzval) {
++	switch (Z_TYPE_P(pzval)) {
++		case IS_ARRAY: {
++			HashTable *tmp = zend_array_dup(Z_ARRVAL_P(pzval));
++
++			zend_hash_apply(tmp, pthreads_store_remove_complex);
++
++			zval_ptr_dtor(pzval);
++
++			ZVAL_ARR(pzval, tmp);
++		} break;
++
++		case IS_OBJECT:
++			if (instanceof_function(Z_OBJCE_P(pzval), pthreads_threaded_entry))
++				break;
++
++		case IS_RESOURCE:
++			return ZEND_HASH_APPLY_REMOVE; 
++	}
++
++	return ZEND_HASH_APPLY_KEEP;
++} /* }}} */
++
++/* {{{ */
++static int pthreads_store_tostring(zval *pzval, char **pstring, size_t *slength, zend_bool complex) {
++	int result = FAILURE;
++	if (pzval && (Z_TYPE_P(pzval) != IS_NULL)) {
++		smart_str smart;
++		HashTable *tmp = NULL;
++		zval ztmp;
++
++		memset(&smart, 0, sizeof(smart_str));
++
++		if (Z_TYPE_P(pzval) == IS_ARRAY && !complex) {
++			tmp = zend_array_dup(Z_ARRVAL_P(pzval));
++
++			ZVAL_ARR(&ztmp, tmp);
++			pzval = &ztmp;
++			
++			zend_hash_apply(tmp, pthreads_store_remove_complex);
++		}
++
++		if ((Z_TYPE_P(pzval) != IS_OBJECT) ||
++			(Z_OBJCE_P(pzval)->serialize != zend_class_serialize_deny)) {
++			php_serialize_data_t vars;
++
++			PHP_VAR_SERIALIZE_INIT(vars);
++			php_var_serialize(&smart, pzval, &vars);
++			PHP_VAR_SERIALIZE_DESTROY(vars);
++
++			if (EG(exception)) {
++				smart_str_free(&smart);
++
++				if (tmp) {
++					zval_dtor(&ztmp);
++				}
++
++				*pstring = NULL;
++				*slength = 0;
++				return FAILURE;			
++			}
++		}
++
++		if (tmp) {
++			zval_dtor(&ztmp);
++		}
++
++		if (smart.s) {
++			*slength = smart.s->len;
++			if (*slength) {
++				*pstring = malloc(*slength+1);
++				if (*pstring) {
++					memcpy(
++						(char*) *pstring, (const void*) smart.s->val, smart.s->len
++					);
++					(*pstring)[*slength] = 0;
++					result = SUCCESS;
++				}
++			} else *pstring = NULL;
++		}
++		
++		smart_str_free(&smart);	
++	} else {
++	    *slength = 0;
++	    *pstring = NULL;
++	}
++	return result;
++} /* }}} */
++
++/* {{{ */
++static int pthreads_store_tozval(zval *pzval, char *pstring, size_t slength) {
++	int result = SUCCESS;
++	
++	if (pstring) {
++		const unsigned char* pointer = (const unsigned char*) pstring;
++
++		if (pointer) {
++			php_unserialize_data_t vars;
++
++			PHP_VAR_UNSERIALIZE_INIT(vars);
++			if (!php_var_unserialize(pzval, &pointer, pointer+slength, &vars)) {
++				result = FAILURE;
++			}
++			PHP_VAR_UNSERIALIZE_DESTROY(vars);
++		} else result = FAILURE;
++	} else result = FAILURE;
++	
++	return result;
++} /* }}} */
++
++/* {{{ */
++int pthreads_store_merge(zval *destination, zval *from, zend_bool overwrite) {
++    if (Z_TYPE_P(from) != IS_ARRAY && 
++        Z_TYPE_P(from) != IS_OBJECT) {
++        return FAILURE;
++    }
++
++    switch (Z_TYPE_P(from)) {
++        case IS_OBJECT: {
++            if (IS_PTHREADS_OBJECT(from)) {
++                pthreads_object_t* threaded[2] = {PTHREADS_FETCH_FROM(Z_OBJ_P(destination)), PTHREADS_FETCH_FROM(Z_OBJ_P(from))};
++                
++                if (pthreads_monitor_lock(threaded[0]->monitor)) {
++                    if (pthreads_monitor_lock(threaded[1]->monitor)) {
++                        HashPosition position;
++                        pthreads_storage *storage;
++                        HashTable *tables[2] = {threaded[0]->store.props, threaded[1]->store.props};
++						zval key;
++
++                        for (zend_hash_internal_pointer_reset_ex(tables[1], &position);
++                             (storage = zend_hash_get_current_data_ptr_ex(tables[1], &position));
++                             zend_hash_move_forward_ex(tables[1], &position)) {
++							zend_hash_get_current_key_zval_ex(tables[1], &key, &position);
++
++                            if (!overwrite) {
++								if (Z_TYPE(key) == IS_LONG) {
++									if (zend_hash_index_exists(tables[0], Z_LVAL(key)))
++										continue;
++								} else {
++									if (zend_hash_exists(tables[0], Z_STR(key)))
++										continue;		
++								}
++                            }
++
++							if (pthreads_store_is_immutable(destination, &key)) {
++								break;
++							}
++							
++                            if (storage->type != IS_RESOURCE) {
++                                pthreads_storage *copy = malloc(sizeof(pthreads_storage));
++
++								memcpy(copy, storage, sizeof(pthreads_storage));
++
++                                switch (copy->type) {
++                                    case IS_STRING:
++                                    case IS_OBJECT:
++			        				case IS_ARRAY: if (storage->length) {
++                                        copy->data = (char*) malloc(copy->length+1);
++										if (!copy->data) {
++											break;
++										}
++										memcpy(copy->data, (const void*) storage->data, copy->length);
++										((char *)copy->data)[copy->length] = 0;
++                                	} break;
++                                }
++
++                                if (Z_TYPE(key) == IS_LONG) {
++									zend_hash_index_update_ptr(tables[0], Z_LVAL(key), copy);
++								} else zend_hash_update_ptr(tables[0], Z_STR(key), copy);
++                            }
++                        }
++
++                        pthreads_monitor_unlock(threaded[1]->monitor);
++                    }
++                    
++                    pthreads_monitor_unlock(threaded[0]->monitor);
++                    
++                    return SUCCESS;
++                    
++                } else return FAILURE;
++            }
++        }
++        
++        /* fall through on purpose to handle normal objects and arrays */
++        
++        default: {
++           pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(destination));
++           
++           if (pthreads_monitor_lock(threaded->monitor)) {
++               HashPosition position;
++               zval *pzval;
++               int32_t index = 0;
++               HashTable *table = (Z_TYPE_P(from) == IS_ARRAY) ? Z_ARRVAL_P(from) : Z_OBJPROP_P(from);
++               
++               for (zend_hash_internal_pointer_reset_ex(table, &position);
++                    (pzval = zend_hash_get_current_data_ex(table, &position));
++                    zend_hash_move_forward_ex(table, &position)) {
++                    zval key;
++
++					zend_hash_get_current_key_zval_ex(table, &key, &position);
++					if (Z_TYPE(key) == IS_STRING)
++						zend_string_delref(Z_STR(key));
++                    
++					switch (Z_TYPE(key)) {
++						case IS_LONG:
++		                    if (!overwrite && zend_hash_index_exists(threaded->store.props, Z_LVAL(key))) {
++		                        goto next;
++		                    }
++		                    pthreads_store_write(destination, &key, pzval);
++						break;
++
++                        case IS_STRING:
++							if (!overwrite && zend_hash_exists(threaded->store.props, Z_STR(key))) {
++                                goto next;
++                            }
++                            pthreads_store_write(destination, &key, pzval);
++						break;
++                    }
++
++next:
++                    index++;
++               }
++               
++               pthreads_monitor_unlock(threaded->monitor);
++           }
++        } break;
++    }
++    
++    return SUCCESS;
++} /* }}} */
++
++
++/* {{{ Will free store element */
++void pthreads_store_storage_dtor (pthreads_storage *storage){
++	if (storage) {
++		switch (storage->type) {
++			case IS_CLOSURE:
++			case IS_OBJECT:
++			case IS_STRING:
++			case IS_ARRAY:
++			case IS_RESOURCE:
++				if (storage->data) {
++					free(storage->data);
++				}
++			break;
++		}
++		free(storage);
++	}
++} /* }}} */
++
++/* {{{ iteration helpers */
++void pthreads_store_reset(zval *object, HashPosition *position) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		zend_hash_internal_pointer_reset_ex(threaded->store.props, position);
++		if (zend_hash_has_more_elements_ex(threaded->store.props, position) == FAILURE) { //empty
++			*position = HT_INVALID_IDX;
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++}
++
++void pthreads_store_key(zval *object, zval *key, HashPosition *position) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++	zend_string *str_key;
++	zend_ulong num_key;
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		switch (zend_hash_get_current_key_ex(threaded->store.props, &str_key, &num_key, position)) {
++			case HASH_KEY_NON_EXISTENT:
++				ZVAL_NULL(key);
++			break;
++			case HASH_KEY_IS_LONG:
++				ZVAL_LONG(key, num_key);
++			break;
++			case HASH_KEY_IS_STRING:
++				ZVAL_STR(key, zend_string_dup(str_key, 0));
++			break;
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++}
++
++void pthreads_store_data(zval *object, zval *value, HashPosition *position) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		pthreads_storage *storage = (pthreads_storage*) 
++			zend_hash_get_current_data_ptr_ex(threaded->store.props, position);
++
++		if (storage) {
++			pthreads_store_convert(storage, value);
++		} else ZVAL_UNDEF(value);
++		
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++}
++
++void pthreads_store_forward(zval *object, HashPosition *position) {
++	pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object));
++
++	if (pthreads_monitor_lock(threaded->monitor)) {
++		zend_hash_move_forward_ex(
++			threaded->store.props, position);
++		if (zend_hash_has_more_elements_ex(threaded->store.props, position) == FAILURE) {
++			*position = HT_INVALID_IDX;
++		}
++		pthreads_monitor_unlock(threaded->monitor);
++	}
++} /* }}} */
++
++#endif
+diff --git a/ext/pthreads/src/store.h b/ext/pthreads/src/store.h
+new file mode 100644
+index 00000000..f8075713
+--- /dev/null
++++ b/ext/pthreads/src/store.h
+@@ -0,0 +1,67 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_STORE_H
++#define HAVE_PTHREADS_STORE_H
++
++#ifdef HAVE_CONFIG_H
++#	include <config.h>
++#endif
++
++#define IS_CLOSURE  (IS_PTR + 1)
++#define IS_PTHREADS (IS_PTR + 2)
++
++typedef HashTable pthreads_store_t;
++
++typedef struct _pthreads_storage {
++	zend_uchar 	type;
++	size_t 	length;
++	zend_bool 	exists;
++	union {
++		zend_long   lval;
++		double     dval;
++	} simple;
++	void    	*data;
++} pthreads_storage;
++
++pthreads_store_t* pthreads_store_alloc();
++void pthreads_store_sync(zval *object);
++int pthreads_store_merge(zval *destination, zval *from, zend_bool overwrite);
++int pthreads_store_delete(zval *object, zval *key);
++int pthreads_store_read(zval *object, zval *key, int type, zval *read);
++zend_bool pthreads_store_isset(zval *object, zval *key, int has_set_exists);
++int pthreads_store_write(zval *object, zval *key, zval *write);
++int pthreads_store_separate(zval *pzval, zval *seperated, zend_bool complex);
++void pthreads_store_separate_zval(zval *pzval);
++void pthreads_store_tohash(zval *object, HashTable *hash);
++int pthreads_store_shift(zval *object, zval *member);
++int pthreads_store_chunk(zval *object, zend_long size, zend_bool preserve, zval *chunk);
++int pthreads_store_pop(zval *object, zval *member);
++int pthreads_store_count(zval *object, zend_long *count);
++void pthreads_store_free(pthreads_store_t *store);
++
++/* {{{ * iteration helpers */
++void pthreads_store_reset(zval *object, HashPosition *position);
++void pthreads_store_key(zval *object, zval *key, HashPosition *position);
++void pthreads_store_data(zval *object, zval *value, HashPosition *position);
++void pthreads_store_forward(zval *object, HashPosition *position); /* }}} */
++
++pthreads_storage* pthreads_store_create(zval *pzval, zend_bool complex);
++int pthreads_store_convert(pthreads_storage *storage, zval *pzval);
++void pthreads_store_storage_dtor(pthreads_storage *element);
++
++#endif
+diff --git a/ext/pthreads/src/thread.h b/ext/pthreads/src/thread.h
+new file mode 100644
+index 00000000..e8c37d57
+--- /dev/null
++++ b/ext/pthreads/src/thread.h
+@@ -0,0 +1,131 @@
++/*
++  +----------------------------------------------------------------------+
++  | pthreads                                                             |
++  +----------------------------------------------------------------------+
++  | Copyright (c) Joe Watkins 2012 - 2015                                |
++  +----------------------------------------------------------------------+
++  | 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.               |
++  +----------------------------------------------------------------------+
++  | Author: Joe Watkins <krakjoe@php.net>                                |
++  +----------------------------------------------------------------------+
++ */
++#ifndef HAVE_PTHREADS_THREAD_H
++#define HAVE_PTHREADS_THREAD_H
++
++#ifndef HAVE_PTHREADS_H
++#	include <src/pthreads.h>
++#endif
++
++#ifndef HAVE_PTHREADS_RESOURCES_H
++#	include <src/resources.h>
++#endif
++
++#ifndef HAVE_PTHREADS_SOCKET_H
++#	include <src/socket.h>
++#endif
++
++#ifndef HAVE_PTHREADS_STORE_H
++#	include <src/store.h>
++#endif
++
++typedef struct _pthreads_ident_t {
++	zend_ulong id;
++	void*** ls;
++} pthreads_ident_t;
++
++/* {{{ */
++typedef struct _pthreads_object_t {
++	pthread_t thread;
++	uint scope;
++	zend_ulong options;
++	pthreads_monitor_t	*monitor;
++	union {
++		pthreads_store_t	*props;
++		pthreads_socket_t	*sock;
++	} store;
++	pthreads_storage    *user_exception_handler;
++	pthreads_stack_t    *stack;
++	pthreads_ident_t 	creator;
++	pthreads_ident_t	local;
++	zend_object         **running;
++	zend_object std;
++} pthreads_object_t; /* }}} */
++
++/* {{{ */
++typedef struct _pthreads_routine_arg_t {
++	pthreads_object_t *thread;
++	pthreads_monitor_t *ready;
++} pthreads_routine_arg_t; /* }}} */
++
++/* {{{ object iterator structure */
++typedef struct _pthreads_iterator_t {
++    zend_object_iterator zit;
++	zval                 object;
++    HashPosition         position;
++} pthreads_iterator_t; /* }}} */
++
++static inline pthreads_object_t* _pthreads_fetch_object(zend_object *object) {
++	return (pthreads_object_t*) ((char*)object - XtOffsetOf(pthreads_object_t, std));
++}
++
++/* {{{ fetches a PTHREAD from a specific object in the current context */
++#define PTHREADS_FETCH_FROM(object) _pthreads_fetch_object(object) /* }}} */
++
++/* {{{ fetches the current PTHREAD from $this */
++#define PTHREADS_FETCH PTHREADS_FETCH_FROM(Z_OBJ(EX(This))) /* }}} */
++
++/* {{{ option constants */
++#define PTHREADS_INHERIT_NONE      0x00000000
++#define PTHREADS_INHERIT_INI       0x00000001
++#define PTHREADS_INHERIT_CONSTANTS 0x00000010
++#define PTHREADS_INHERIT_FUNCTIONS 0x00000100
++#define PTHREADS_INHERIT_CLASSES   0x00001000
++#define PTHREADS_INHERIT_INCLUDES  0x00010000
++#define PTHREADS_INHERIT_COMMENTS  0x00100000
++#define PTHREADS_INHERIT_ALL       0x00111111
++#define PTHREADS_ALLOW_HEADERS	   0x10000000 /* }}} */
++
++/* {{{ scope constants */
++#define PTHREADS_SCOPE_UNKNOWN     (0)
++#define PTHREADS_SCOPE_THREADED    (1<<1)
++#define PTHREADS_SCOPE_THREAD      (1<<2)
++#define PTHREADS_SCOPE_WORKER      (1<<3)
++#define PTHREADS_SCOPE_SOCKET	   (1<<4)
++#define PTHREADS_SCOPE_CONNECTION  (1<<5) /* }}} */
++
++/* {{{ scope macros */
++#define PTHREADS_IS_KNOWN_ENTRY(t)      ((t)->scope)
++#define PTHREADS_IS_CONNECTION(t)       ((t)->scope & PTHREADS_SCOPE_CONNECTION)
++#define PTHREADS_IS_NOT_CONNECTION(t)   (!PTHREADS_IS_CONNECTION(t))
++#define PTHREADS_IS_SOCKET(t)       	((t)->scope & PTHREADS_SCOPE_SOCKET)
++#define PTHREADS_IS_NOT_SOCKET(t)   	(!PTHREADS_IS_SOCKET(t))
++#define PTHREADS_IS_THREAD(t)           ((t)->scope & PTHREADS_SCOPE_THREAD)
++#define PTHREADS_IS_NOT_THREAD(t)       (!PTHREADS_IS_THREAD(t))
++#define PTHREADS_IS_WORKER(t)           ((t)->scope & PTHREADS_SCOPE_WORKER)
++#define PTHREADS_IS_NOT_WORKER(t)       (!PTHREADS_IS_WORKER(t))
++#define PTHREADS_IS_THREADED(t)         ((t)->scope & PTHREADS_SCOPE_THREADED)
++#define PTHREADS_IS_NOT_THREADED(t)     (!PTHREADS_IS_THREADED(t)) /* }}} */
++
++/* {{{ pthread_self wrapper */
++static inline ulong pthreads_self() {
++#ifdef _WIN32
++	return (ulong) GetCurrentThreadId();
++#else
++	return (ulong) pthread_self();
++#endif
++} /* }}} */
++
++/* {{{ tell if the calling thread created referenced PTHREAD */
++#define PTHREADS_IN_CREATOR(t)	((t)->creator.ls == TSRMLS_CACHE) /* }}} */
++
++/* {{{ tell if the referenced thread is the threading context */
++#define PTHREADS_IN_THREAD(t)	((t)->local.ls == TSRMLS_CACHE) /* }}} */
++
++#endif /* HAVE_PTHREADS_THREAD_H */
++
+diff --git a/ext/pthreads/tests/anon-bound-inherited.phpt b/ext/pthreads/tests/anon-bound-inherited.phpt
+new file mode 100644
+index 00000000..f1905319
+--- /dev/null
++++ b/ext/pthreads/tests/anon-bound-inherited.phpt
+@@ -0,0 +1,34 @@
++--TEST--
++Test anonymous classes (bound inherited class)
++--DESCRIPTION--
++This test verifies that anonymous Threaded objects work as expected
++--FILE--
++<?php
++$worker = new Worker();
++
++$collectable = new class extends Threaded {
++	/** z */
++	const Z = 1;
++	/** a */
++	static $a = 2;
++	/** c */
++	public $c = false;
++
++	public function run() {
++		var_dump(
++			$this instanceof Collectable,
++			self::Z,
++			self::$a,
++			$this->c
++		);
++	}
++};
++
++$worker->start();
++$worker->stack($collectable);
++$worker->shutdown();
++--EXPECT--
++bool(true)
++int(1)
++int(2)
++bool(false)
+diff --git a/ext/pthreads/tests/anon-interceptors.phpt b/ext/pthreads/tests/anon-interceptors.phpt
+new file mode 100644
+index 00000000..59b08641
+--- /dev/null
++++ b/ext/pthreads/tests/anon-interceptors.phpt
+@@ -0,0 +1,57 @@
++--TEST--
++Test interceptors of anonymous classes
++--DESCRIPTION--
++This test verifies that interceptors of anonymous Thread objects work as expected
++--FILE--
++<?php
++class Foo extends Threaded {
++    public $bar;
++
++    public function __construct() {
++        var_dump(__METHOD__);
++
++        $this->bar = new class extends Threaded {
++            private $buzz;
++
++            public function __construct() {
++                var_dump('Bar::__construct');
++            }
++            public function __destruct() {
++                var_dump('Bar::__destruct');
++            }
++            public function __set($name, $value) {
++                var_dump($name, $value);
++                $this->buzz = $value;
++            }
++            public function __get($name) {
++                return $this->buzz;
++            }
++        };
++        $this->bar->buzz = 'hello world';
++        var_dump($this->bar->buzz);
++    }
++    public function __destruct() {
++        var_dump(__METHOD__);
++    }
++}
++
++class Test extends \Thread {
++    public function run() {
++        $foo = new Foo();
++        var_dump($foo->bar);
++    }
++}
++$thread = new Test();
++$thread->start() && $thread->join();
++--EXPECT--
++string(16) "Foo::__construct"
++string(16) "Bar::__construct"
++string(4) "buzz"
++string(11) "hello world"
++string(11) "hello world"
++object(class@anonymous)#3 (1) {
++  ["buzz"]=>
++  string(11) "hello world"
++}
++string(15) "Foo::__destruct"
++string(15) "Bar::__destruct"
+diff --git a/ext/pthreads/tests/anon-unbound-double-inherited.phpt b/ext/pthreads/tests/anon-unbound-double-inherited.phpt
+new file mode 100644
+index 00000000..6f29a527
+--- /dev/null
++++ b/ext/pthreads/tests/anon-unbound-double-inherited.phpt
+@@ -0,0 +1,44 @@
++--TEST--
++Test anonymous classes (unbound double inherited class)
++--DESCRIPTION--
++This test verifies that anonymous Threaded objects (with double inheritance)
++work as expected
++--FILE--
++<?php
++
++/** c */
++class C extends Threaded
++{
++    use T1, T2 {
++        T2::t insteadof T1;
++        T1::t as t2;
++    }
++    const C = 0;
++    public $c2 = 2;
++    public function __construct(){}
++    public function __destruct(){}
++    public function __call($a, $b){}
++    public static function __callStatic($a, $b){}
++    public function __set($p, $v){}
++    public function __isset($p){}
++    public function __unset($p){}
++    public function __sleep(){}
++    public function __wakeup(){}
++    public function __toString(){}
++    public function __invoke(){}
++    public function __set_state($ps){}
++    public function __clone(){}
++    public function __debugInfo(){}
++    public function run(){var_dump(self::C, $this->c2);}
++}
++
++trait T1 {function t(){}}
++trait T2 {function t(){}}
++
++$w = new Worker();
++$w->start();
++$w->stack(/** b */new class extends C {});
++$w->shutdown();
++--EXPECT--
++int(0)
++int(2)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/anon-unbound-inherited.phpt b/ext/pthreads/tests/anon-unbound-inherited.phpt
+new file mode 100644
+index 00000000..79a99d1d
+--- /dev/null
++++ b/ext/pthreads/tests/anon-unbound-inherited.phpt
+@@ -0,0 +1,35 @@
++--TEST--
++Test anonymous classes (unbound inherited class)
++--DESCRIPTION--mae
++This test verifies that anonymous Threaded objects work as expected
++--FILE--
++<?php
++$worker = new Worker();
++
++$worker->start();
++
++$collectable = new class extends Threaded {
++	/** z */
++	const Z = 1;
++	/** a */
++	static $a = 2;
++	/** c */
++	public $c = false;
++
++	public function run() {
++		var_dump(
++			$this instanceof Collectable,
++			self::Z,
++			self::$a,
++			$this->c
++		);
++	}
++};
++
++$worker->stack($collectable);
++$worker->shutdown();
++--EXPECT--
++bool(true)
++int(1)
++int(2)
++bool(false)
+diff --git a/ext/pthreads/tests/anon-unbound-use-guards.phpt b/ext/pthreads/tests/anon-unbound-use-guards.phpt
+new file mode 100644
+index 00000000..4537c0e7
+--- /dev/null
++++ b/ext/pthreads/tests/anon-unbound-use-guards.phpt
+@@ -0,0 +1,24 @@
++--TEST--
++Test the guard usage in unbound anonymous classes
++--DESCRIPTION--
++This test verifies that class entry flags are properly copied when completing a
++class copy (it is only relevant to unbound anonymous classes).
++--FILE--
++<?php
++
++class T2 extends Threaded
++{
++    public function __get($p){}
++
++	public function run()
++	{
++        var_dump($this->prop);
++	}
++}
++
++$w = new Worker();
++$w->start();
++$w->stack(new class extends T2 {});
++$w->shutdown();
++--EXPECT--
++NULL
+diff --git a/ext/pthreads/tests/binding.phpt b/ext/pthreads/tests/binding.phpt
+new file mode 100644
+index 00000000..94f34939
+--- /dev/null
++++ b/ext/pthreads/tests/binding.phpt
+@@ -0,0 +1,55 @@
++--TEST--
++Test pthreads connections
++--DESCRIPTION--
++This test verifies that variables are bound properly by pthreads
++--FILE--
++<?php
++class ThreadTesting extends Thread {
++	public $other;
++	public $done;
++	
++	public function setOther($other){
++		$this->other = $other;
++	}
++	public function run(){
++		$this->synchronized(function($that) {
++			$that->done = true;
++			$that->notify();
++		}, $this);
++	}
++}
++
++class ThreadTest extends Thread {
++	public $other;
++	public $done;
++	
++	public function setOther($other){
++		$this->other = $other;
++	}
++	public function run(){
++		$this->synchronized(function($that) {
++			$that->done = true;
++			$that->notify();
++		}, $this);
++	}
++}
++
++$threads[0]=new ThreadTesting();
++$threads[1]=new ThreadTest();
++$threads[0]->setOther($threads[1]);
++$threads[1]->setOther($threads[0]);
++foreach($threads as $thread)
++	$thread->start();
++foreach($threads as $thread) {
++	$thread->synchronized(function() use($thread){
++		if (!$thread->done)
++			var_dump($thread->wait());
++		else var_dump($thread->done);
++	});
++}
++foreach ($threads as $thread)
++	$thread->join();
++?>
++--EXPECT--
++bool(true)
++bool(true)
+diff --git a/ext/pthreads/tests/callstatic-etc.phpt b/ext/pthreads/tests/callstatic-etc.phpt
+new file mode 100644
+index 00000000..502bd39f
+--- /dev/null
++++ b/ext/pthreads/tests/callstatic-etc.phpt
+@@ -0,0 +1,28 @@
++--TEST--
++Testing extended object functionality, magic methods bug #34
++--DESCRIPTION--
++Test that the fix for bug #34 is a success
++--FILE--
++<?php
++class Test {
++    public static function __callStatic ($name, $args) {
++        var_dump($name, $args);
++    }
++}
++
++class UserThread extends Thread {
++    public function run () {
++        Test::called_func("argument");
++    }
++}
++
++$thread = new UserThread;
++$thread->start();
++?>
++--EXPECT--
++string(11) "called_func"
++array(1) {
++  [0]=>
++  string(8) "argument"
++}
++
+diff --git a/ext/pthreads/tests/child-to-parent-class-copying.phpt b/ext/pthreads/tests/child-to-parent-class-copying.phpt
+new file mode 100644
+index 00000000..c7256729
+--- /dev/null
++++ b/ext/pthreads/tests/child-to-parent-class-copying.phpt
+@@ -0,0 +1,33 @@
++--TEST--
++Test the copying of child to parent thread
++--DESCRIPTION--
++When copying a class from a child thread to a parent thread, there are problems
++with interned strings (since they were not being copied).
++--FILE--
++<?php
++
++class Foo extends Thread
++{
++	public $running = true;
++	private $shared;
++
++	public function run() {
++		require __DIR__ . '/child-to-parent-class-copying-helper.php';
++
++		$this->shared['baseClass'] = new ExternalBaseClass();
++
++		while($this->running);
++	}
++}
++
++$foo = new Foo();
++$foo->shared = new Threaded();
++$foo->start();
++
++while(!isset($foo->shared['baseClass']));
++
++$baseClass = $foo->shared['baseClass']; // copy zend_class_entry
++$foo->running = false;
++
++$foo->join();
++--EXPECT--
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/chunk.phpt b/ext/pthreads/tests/chunk.phpt
+new file mode 100644
+index 00000000..9f17c9fe
+--- /dev/null
++++ b/ext/pthreads/tests/chunk.phpt
+@@ -0,0 +1,40 @@
++--TEST--
++Check chunking
++--DESCRIPTION--
++This test verifies functionality of ::chunk
++--FILE--
++<?php
++class S extends Threaded {
++    public function run(){}
++}
++
++$s = new S();
++$s->merge(array_fill(0, 10000, true));
++
++var_dump($s->chunk(10));
++?>
++--EXPECT--
++array(10) {
++  [0]=>
++  bool(true)
++  [1]=>
++  bool(true)
++  [2]=>
++  bool(true)
++  [3]=>
++  bool(true)
++  [4]=>
++  bool(true)
++  [5]=>
++  bool(true)
++  [6]=>
++  bool(true)
++  [7]=>
++  bool(true)
++  [8]=>
++  bool(true)
++  [9]=>
++  bool(true)
++}
++
++
+diff --git a/ext/pthreads/tests/class-defaults.phpt b/ext/pthreads/tests/class-defaults.phpt
+new file mode 100644
+index 00000000..542ddc21
+--- /dev/null
++++ b/ext/pthreads/tests/class-defaults.phpt
+@@ -0,0 +1,51 @@
++--TEST--
++Test class defaults
++--DESCRIPTION--
++Class defaults should now initialize defaults properly
++--FILE--
++<?php
++class Test extends Thread {
++
++	public function run(){
++		var_dump($this);
++	}
++	
++	protected $string = "hello world";
++	protected $array  = array(1, 2, 3);
++	private $pstring  = "world hello";
++	private $parray   = array(3, 2, 1);
++	protected static $nocopy = true;
++}
++
++$test =new Test();
++$test->string = strrev($test->string);
++$test->start();
++$test->join();
++?>
++--EXPECTF--
++object(Test)#%d (%d) {
++  ["string"]=>
++  string(11) "dlrow olleh"
++  ["array"]=>
++  object(Volatile)#%d (%d) {
++    [0]=>
++    int(1)
++    [1]=>
++    int(2)
++    [2]=>
++    int(3)
++  }
++  ["pstring"]=>
++  string(11) "world hello"
++  ["parray"]=>
++  object(Volatile)#%d (%d) {
++    [0]=>
++    int(3)
++    [1]=>
++    int(2)
++    [2]=>
++    int(1)
++  }
++}
++
++
+diff --git a/ext/pthreads/tests/class-static-properties-2.phpt b/ext/pthreads/tests/class-static-properties-2.phpt
+new file mode 100644
+index 00000000..ab1ac0ce
+--- /dev/null
++++ b/ext/pthreads/tests/class-static-properties-2.phpt
+@@ -0,0 +1,24 @@
++--TEST--
++Testing class statics properties
++--DESCRIPTION--
++Ensure that the static properties of the current thread is copied and available.
++--FILE--
++<?php
++
++class T extends Thread {
++	private static $autoloadPath = 'def';
++
++	public function __construct() {
++		self::$autoloadPath = 'abc';
++    }
++
++	public function run() {
++		var_dump(self::$autoloadPath);
++	}
++}
++
++$thread = new T();
++$thread->start(PTHREADS_INHERIT_NONE) && $thread->join();
++
++--EXPECT--
++string(3) "abc"
+diff --git a/ext/pthreads/tests/class-static-properties-double-inheritance.phpt b/ext/pthreads/tests/class-static-properties-double-inheritance.phpt
+new file mode 100644
+index 00000000..ceae2399
+--- /dev/null
++++ b/ext/pthreads/tests/class-static-properties-double-inheritance.phpt
+@@ -0,0 +1,32 @@
++--TEST--
++Test static properties of classes
++--DESCRIPTION--
++This is yet another variation of static properties that has highlighted regressions
++--FILE--
++<?php
++
++$worker = new \Worker();
++$worker->start();
++
++class TestAsyncTask extends ManInTheMiddle {
++	public static $destroyed = false;
++
++	public function run()
++	{
++		try {
++			var_dump(self::$destroyed);
++		} catch(\Error $e) {
++			var_dump($e->getMessage());
++		}
++	}
++}
++
++abstract class ManInTheMiddle extends \Threaded{}
++
++error_reporting(-1);
++
++$worker->stack(new TestAsyncTask());
++while($worker->collect());
++$worker->shutdown();
++--EXPECT--
++bool(false)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/class-static-properties-order-dependencies-inheritance.phpt b/ext/pthreads/tests/class-static-properties-order-dependencies-inheritance.phpt
+new file mode 100644
+index 00000000..0b6eb00a
+--- /dev/null
++++ b/ext/pthreads/tests/class-static-properties-order-dependencies-inheritance.phpt
+@@ -0,0 +1,29 @@
++--TEST--
++Testing inherited class statics properties and dependencies (gh bug #666)
++--DESCRIPTION--
++If a inherited static property holds a reference to a threaded object, the order of class definition is important.
++--FILE--
++<?php
++
++class SecondClass extends FirstClass {}
++class FirstClass extends \Threaded
++{
++	public static $prop = [];
++}
++
++FirstClass::$prop[] = new SecondClass();
++
++$thread = new class extends Thread {
++    public function run() {
++        var_dump(FirstClass::$prop);
++    }
++};
++
++$thread->start() && $thread->join();
++--EXPECT--
++array(1) {
++  [0]=>
++  object(SecondClass)#1 (0) {
++  }
++}
++
+diff --git a/ext/pthreads/tests/class-static-properties-order-dependencies.phpt b/ext/pthreads/tests/class-static-properties-order-dependencies.phpt
+new file mode 100644
+index 00000000..be341a7b
+--- /dev/null
++++ b/ext/pthreads/tests/class-static-properties-order-dependencies.phpt
+@@ -0,0 +1,29 @@
++--TEST--
++Testing class statics properties and dependencies (gh bug #666)
++--DESCRIPTION--
++If a static property holds a reference to a threaded object, the order of class definition is important.
++--FILE--
++<?php
++class FirstClass extends \Threaded
++{
++    public static $prop = [];
++}
++
++class SecondClass extends \Threaded {}
++
++FirstClass::$prop[] = new SecondClass();
++
++$thread = new class extends Thread {
++    public function run() {
++        var_dump(FirstClass::$prop);
++    }
++};
++
++$thread->start() && $thread->join();
++--EXPECT--
++array(1) {
++  [0]=>
++  object(SecondClass)#1 (0) {
++  }
++}
++
+diff --git a/ext/pthreads/tests/class-static-properties.phpt b/ext/pthreads/tests/class-static-properties.phpt
+new file mode 100644
+index 00000000..c009483f
+--- /dev/null
++++ b/ext/pthreads/tests/class-static-properties.phpt
+@@ -0,0 +1,120 @@
++--TEST--
++Testing class statics properties (gh bug #570)
++--DESCRIPTION--
++Class static properties were messy, extremely messy in PHP 7 so far.
++
++We are back to the original rules, that complex unsafe members will be removed from statics:
++
++	normal PHP class: removed
++	threaded class: remains
++	closure: remains
++	resource: removed
++	array: remains, rules above applied
++--FILE--
++<?php
++class Test extends Threaded {
++
++   public static $prop1 = "one";
++   public static $prop2 = [1, 2, 3];
++   public static $prop3 = [[1,2,3], [4,5,6]];
++   public static $prop4;
++   public static $prop5;
++   public static $prop6;
++   public static $prop7;
++   public static $prop8;
++   public static $prop9;
++}
++
++Test::$prop4 = new Threaded;
++Test::$prop5 = new stdClass; /* will be null */
++Test::$prop6 = function() { /* will be copied with voodoo */
++	return [1,2,3];
++};
++
++Test::$prop7 = fopen("php://stdin", "r");
++Test::$prop8 = [fopen("php://stdin", "r")];
++Test::$prop9 = [new Threaded, new stdClass, [
++	new Threaded, new stdClass, [
++		new Threaded, new stdClass
++	]
++]];
++
++$test = new class extends Thread {
++	public function run() {
++		var_dump(Test::$prop1,
++				 Test::$prop2,
++				 Test::$prop3,
++				 Test::$prop4,
++				 Test::$prop5,
++				 Test::$prop6, (Test::$prop6)(),
++				 Test::$prop7,
++				 Test::$prop8,
++				 Test::$prop9);
++	}
++}; 
++
++$test->start() && $test->join();
++--EXPECT--
++string(3) "one"
++array(3) {
++  [0]=>
++  int(1)
++  [1]=>
++  int(2)
++  [2]=>
++  int(3)
++}
++array(2) {
++  [0]=>
++  array(3) {
++    [0]=>
++    int(1)
++    [1]=>
++    int(2)
++    [2]=>
++    int(3)
++  }
++  [1]=>
++  array(3) {
++    [0]=>
++    int(4)
++    [1]=>
++    int(5)
++    [2]=>
++    int(6)
++  }
++}
++object(Threaded)#1 (0) {
++}
++NULL
++object(Closure)#2 (0) {
++}
++array(3) {
++  [0]=>
++  int(1)
++  [1]=>
++  int(2)
++  [2]=>
++  int(3)
++}
++NULL
++array(0) {
++}
++array(2) {
++  [0]=>
++  object(Threaded)#3 (0) {
++  }
++  [2]=>
++  array(2) {
++    [0]=>
++    object(Threaded)#4 (0) {
++    }
++    [2]=>
++    array(1) {
++      [0]=>
++      object(Threaded)#5 (0) {
++      }
++    }
++  }
++}
++
+diff --git a/ext/pthreads/tests/closure-delayed-include.phpt b/ext/pthreads/tests/closure-delayed-include.phpt
+new file mode 100644
+index 00000000..39103778
+--- /dev/null
++++ b/ext/pthreads/tests/closure-delayed-include.phpt
+@@ -0,0 +1,49 @@
++--TEST--
++Closure copying of an included closure
++--DESCRIPTION--
++We need to copy closures which are defined and bound by included classes
++--FILE--
++<?php
++
++class Foo extends \Thread {
++    /** @var bool */
++    public $running;
++
++    /** @var \Threaded */
++    private $shared;
++
++    public function __construct(\Threaded $shared) {
++        $this->shared = $shared;
++    }
++
++    public function run() {
++        $this->running = true;
++
++        require __DIR__ .'/assets/ExternalClosureDefinition.php';
++
++        $this->shared['loader'] = new ExternalClosureDefinition();
++        $this->synchronized(function () {
++            while($this->running) {
++                $this->wait(0);
++            }
++        });
++     }
++}
++
++$shared = new \Threaded();
++
++$foo = new Foo($shared);
++$foo->start();
++
++while(true) {
++    if(!isset($shared['loader'])) {
++        continue;
++    }
++    $closureDefinition = $shared['loader'];
++    $closureDefinition->load();
++    break;
++}
++$foo->running = false;
++$foo->notify() && $foo->join();
++--EXPECT--
++string(11) "Hello World"
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/closure-secondary-thread-copy.phpt b/ext/pthreads/tests/closure-secondary-thread-copy.phpt
+new file mode 100644
+index 00000000..37a37eb6
+--- /dev/null
++++ b/ext/pthreads/tests/closure-secondary-thread-copy.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Closure copying of an already copied closure
++--DESCRIPTION--
++We need to retain function flag information for closures, since their copying
++semantics differ to that of normal function copying.
++--FILE--
++<?php
++
++class SubThread extends Thread
++{
++    public function run()
++    {
++        $this->testFunction();
++    }
++
++    public function testFunction()
++    {
++        (function () {})();
++    }
++}
++
++$thread = new class extends Thread {
++    public function run()
++    {
++        $thread = new SubThread();
++        $thread->start(PTHREADS_INHERIT_NONE) && $thread->join();
++    }
++};
++
++$thread->start() && $thread->join();
++--EXPECT--
+diff --git a/ext/pthreads/tests/closures-as-members-repeated-reallocation.phpt b/ext/pthreads/tests/closures-as-members-repeated-reallocation.phpt
+new file mode 100644
+index 00000000..7e49c60b
+--- /dev/null
++++ b/ext/pthreads/tests/closures-as-members-repeated-reallocation.phpt
+@@ -0,0 +1,26 @@
++--TEST--
++Testing closure members reallocation
++--DESCRIPTION--
++This test verifies that closures can be set as members repeatedly
++--FILE--
++<?php
++class TestClosure extends \Threaded {
++    protected $closure;
++    function __construct( $closure) {
++        $this->closure = $closure;
++    }
++    public function run() {
++        ($this->closure)();
++    }
++}
++$count = 0;
++$worker = new Worker();
++$worker->start();
++while ($count++ < 1000) {
++    $function = new TestClosure(function() {});
++    $worker->stack($function);
++    while($worker->collect());
++}
++var_dump('ok');
++--EXPECTF--
++string(2) "ok"
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/closures-as-members.phpt b/ext/pthreads/tests/closures-as-members.phpt
+new file mode 100644
+index 00000000..ee377ca1
+--- /dev/null
++++ b/ext/pthreads/tests/closures-as-members.phpt
+@@ -0,0 +1,67 @@
++--TEST--
++Testing closure members
++--DESCRIPTION--
++This test verifies that closures can be set as members and called from anywhere
++--FILE--
++<?php
++$test = new Threaded();
++
++$test->some = function(){
++    echo "Hello Some\n";
++};
++
++class T extends Thread {
++
++    public function __construct($test) {
++        $this->test = $test;
++    }
++    
++    public function run() {
++        /* call original closure */
++        $this->call($this->test->some);
++        
++        /* set new closure */
++        $this->synchronized(function(){
++            $this->set = true;
++            $this->test->some = function() {
++                echo "Hello World\n";
++            };
++            $this->notify();
++        });
++        
++        /* wait for new closure execution */
++        $this->synchronized(function(){
++            while (!$this->used)
++                $this->wait();
++        });
++    }
++    
++    public function call(Closure $closure) {
++        $closure();
++    }
++}
++
++/* start thread to call closure */
++$t = new T($test);
++$t->start();
++
++/* wait for new closure */
++$t->synchronized(function() use($t) {
++    while (!$t->set)
++        $t->wait();
++});
++
++/* call new closure */
++$some = $test->some;
++$some();
++
++/* allow new closure to be dtored with old context */
++$t->synchronized(function() use($t) {
++    $t->used = true;
++    $t->notify();
++});
++$t->join();
++unset($t);
++--EXPECTF--
++Hello Some
++Hello World
+diff --git a/ext/pthreads/tests/complex-statics-set-null.phpt b/ext/pthreads/tests/complex-statics-set-null.phpt
+new file mode 100644
+index 00000000..28b8d0c7
+--- /dev/null
++++ b/ext/pthreads/tests/complex-statics-set-null.phpt
+@@ -0,0 +1,64 @@
++--TEST--
++Test NULLing ressources in arrays
++--FILE--
++<?php
++class file {
++	public static $fps;
++
++	public static function __callstatic($method, $args) {
++		$tid = Thread::getCurrentThreadId();
++		if (isset(self::$fps[$tid])) {
++			return call_user_func_array(array("file", "_{$method}"), array_merge($args, array($tid)));
++		} else {
++			self::$fps[$tid] = fopen(__FILE__, "r+");
++			if (isset(self::$fps[$tid]))
++				return call_user_func_array(array("file", "_{$method}"), array_merge($args, array($tid)));
++		}
++	}
++	
++	public static function _get ($arg, $tid) {
++		printf("%s: %s\n", __METHOD__, $arg);
++		var_dump(self::$fps);
++	}
++}
++
++class UserThread extends Thread {
++	public function run () {
++		/* execute calls */
++		$i = 2;
++		file::get("something".(++$i));
++		file::get("something".(++$i));
++		
++	}
++}
++
++$i = 0;
++
++file::get("something".(++$i));
++file::get("something".(++$i));
++
++$thread = new UserThread();
++$thread->start();
++$thread->join();
++?>
++--EXPECTF--
++file::_get: something1
++array(1) {
++  [%i]=>
++  resource(5) of type (stream)
++}
++file::_get: something2
++array(1) {
++  [%i]=>
++  resource(5) of type (stream)
++}
++file::_get: something3
++array(1) {
++  [%i]=>
++  resource(2) of type (stream)
++}
++file::_get: something4
++array(1) {
++  [%i]=>
++  resource(2) of type (stream)
++}
+diff --git a/ext/pthreads/tests/complex-statics.phpt b/ext/pthreads/tests/complex-statics.phpt
+new file mode 100644
+index 00000000..f88819fc
+--- /dev/null
++++ b/ext/pthreads/tests/complex-statics.phpt
+@@ -0,0 +1,46 @@
++--TEST--
++Test complex statics bug #32
++--DESCRIPTION--
++This test verifies that complex static types are ignored when creating thread contexts, leading to predictable stable behaviour
++--FILE--
++<?php
++class sql {
++	public static $connection;
++
++	public static function __callstatic($method, $args){
++		$tid = Thread::getCurrentThreadId();
++		if (isset(self::$connection[$tid])) {
++			return call_user_func_array(array(self::$connection[$tid], "_{$method}"), $args);
++		} else {
++			self::$connection[$tid] = new sql();
++			if (isset(self::$connection[$tid]))
++				return call_user_func_array(array(self::$connection[$tid], "_{$method}"), $args);
++		}
++	}
++	
++	public function _query($sql){
++		printf("%s: %s\n", __METHOD__, $sql);
++	}
++}
++
++class UserThread extends Thread {
++    public function run () {
++        /* execute queries */
++		sql::query("SELECT * FROM mysql.user");
++		sql::query("SELECT * FROM mysql.user");
++		
++    }
++}
++
++sql::query("SELECT * FROM mysql.user");
++sql::query("SELECT * FROM mysql.user");
++
++$thread = new UserThread();
++$thread->start();
++?>
++--EXPECT--
++sql::_query: SELECT * FROM mysql.user
++sql::_query: SELECT * FROM mysql.user
++sql::_query: SELECT * FROM mysql.user
++sql::_query: SELECT * FROM mysql.user
++
+diff --git a/ext/pthreads/tests/constant-array.phpt b/ext/pthreads/tests/constant-array.phpt
+new file mode 100644
+index 00000000..ba7900ae
+--- /dev/null
++++ b/ext/pthreads/tests/constant-array.phpt
+@@ -0,0 +1,17 @@
++--TEST--
++Test pthreads constant array copy
++--FILE--
++<?php
++const CONSTANT_ARRAY = ['ok'];
++
++class Test extends Thread {
++        public function run() {
++                echo CONSTANT_ARRAY[0];
++        }
++}
++
++$test = new Test();
++$test->start();
++$test->join();
++--EXPECT--
++ok
+diff --git a/ext/pthreads/tests/constant-ast-copy-missing-type.phpt b/ext/pthreads/tests/constant-ast-copy-missing-type.phpt
+new file mode 100644
+index 00000000..85cbb228
+--- /dev/null
++++ b/ext/pthreads/tests/constant-ast-copy-missing-type.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Test constant AST copy bug #564
++--DESCRIPTION--
++AST in op array literals caused corrupted heaps because it wasn't copied properly. 
++--FILE--
++<?php
++namespace any\name\space {
++	class Test extends \Thread {
++		const KEY = '';
++
++		public function run() {
++			static $arrConst = [self::KEY => true];
++
++			var_dump($arrConst);
++		}
++	}
++}
++
++namespace {
++	use any\name\space\Test;
++
++	$objTest = new Test();
++	$objTest->start();
++	$objTest->join();
++}
++?>
++--EXPECTF--
++array(1) {
++  [""]=>
++  bool(true)
++}
+diff --git a/ext/pthreads/tests/constant-ast-copy.phpt b/ext/pthreads/tests/constant-ast-copy.phpt
+new file mode 100644
+index 00000000..aa4153ac
+--- /dev/null
++++ b/ext/pthreads/tests/constant-ast-copy.phpt
+@@ -0,0 +1,30 @@
++--TEST--
++Test constant AST copy bug #495
++--DESCRIPTION--
++AST in op array literals caused corrupted heaps because it wasn't copied properly.
++--FILE--
++<?php
++class Test
++{
++    private static function getClassPropertyNames(
++		string $classname, 
++		$view = \ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) {
++	}
++}
++
++$t = new class extends \Thread {
++    public function run() {
++        throw new \Exception();
++    }
++};
++
++$t->start() && $t->join();
++?>
++--EXPECTF--
++Fatal error: Uncaught Exception in %s:12
++Stack trace:
++#0 [internal function]: class@anonymous->run()
++#1 {main}
++  thrown in %s on line 12
++
++
+diff --git a/ext/pthreads/tests/constants.phpt b/ext/pthreads/tests/constants.phpt
+new file mode 100644
+index 00000000..97786b32
+--- /dev/null
++++ b/ext/pthreads/tests/constants.phpt
+@@ -0,0 +1,15 @@
++--TEST--
++Test constants (bug 19)
++--DESCRIPTION--
++This test verifies that constant members in declarations made outside of threads are available inside threads without error
++--FILE--
++<?php
++class TestThread extends Thread {
++	public function run() { printf("%s\n", DateTime::ISO8601 ); }
++}
++
++$thread = new TestThread();
++$thread->start();
++?>
++--EXPECT--
++Y-m-d\TH:i:sO
+diff --git a/ext/pthreads/tests/corrupted-resources.phpt b/ext/pthreads/tests/corrupted-resources.phpt
+new file mode 100644
+index 00000000..c60aeb4b
+--- /dev/null
++++ b/ext/pthreads/tests/corrupted-resources.phpt
+@@ -0,0 +1,39 @@
++--TEST--
++Testing sane handling of resources and objects bug #39
++--DESCRIPTION--
++Test that resources and objects are not corrupted when written to thread storage
++--FILE--
++<?php
++class Work extends Threaded {
++	public function run(){
++		
++	}
++}
++
++class Test extends Thread {
++	public function run(){
++		$test = new Work();
++		$this->test = $test;
++		print_r($this->test);
++		var_dump($this->test);
++		$stream = tmpfile();
++		var_dump($stream);
++		$this->stream = $stream;
++		stream_set_blocking($this->stream, 0);
++		var_dump($this->stream);
++	}
++}
++
++$test =new Test();
++$test->start();
++?>
++--EXPECTF--
++Work Object
++(
++)
++object(Work)#2 (0) {
++}
++resource(%d) of type (stream)
++resource(%d) of type (stream)
++
++
+diff --git a/ext/pthreads/tests/counting-members.phpt b/ext/pthreads/tests/counting-members.phpt
+new file mode 100644
+index 00000000..3ba30849
+--- /dev/null
++++ b/ext/pthreads/tests/counting-members.phpt
+@@ -0,0 +1,19 @@
++--TEST--
++Testing member count
++--DESCRIPTION--
++This test verifies that getting member counts works
++--FILE--
++<?php
++class Test extends Threaded {
++	public function run() { 
++	}
++}
++
++$t = new Test();
++$t[] = "one";
++$t[] = "two";
++$t["three"] = "three";
++var_dump(count($t));
++?>
++--EXPECT--
++int(3)
+diff --git a/ext/pthreads/tests/crazy-refcounting-stuff.phpt b/ext/pthreads/tests/crazy-refcounting-stuff.phpt
+new file mode 100644
+index 00000000..326e384a
+--- /dev/null
++++ b/ext/pthreads/tests/crazy-refcounting-stuff.phpt
+@@ -0,0 +1,56 @@
++--TEST--
++Test managing refcount in userland (super advanced voodoo shit, leave it alone)
++--DESCRIPTION--
++pthreads v3 does allow the programmer to store references to other Threaded objects as members without references.
++It still might be useful in a complex system to manipulate the refcount of Threaded objects, so this stays.
++
++This is an advanced feature, if you don't know what you're doing, *don't touch it*.
++
++*I will be EXTREMELY reluctant to help with issues caused by the programmer trying to deal with refcounts*
++--FILE--
++<?php
++class Managed extends Threaded {
++	private $owner;
++	
++	public function __construct() {
++		$this->addRef();
++		$this->owner = 
++			Thread::getCurrentThreadId();
++	}
++
++	public function __destruct() {
++		if (Thread::getCurrentThreadId() == $this->owner) {
++			$this->delRef();
++		}
++	}
++}
++
++class Test extends Thread {
++	public function __construct() {
++		$this->inctor = new Managed();
++	}
++
++	public function run() {
++		$this->inrun = new Managed();
++
++		var_dump($this);
++	}
++}
++
++$thread = new Test();
++$thread->start();
++$thread->join();
++--EXPECTF--
++object(Test)#1 (2) {
++  ["inctor"]=>
++  object(Managed)#3 (1) {
++    ["owner"]=>
++    int(%s)
++  }
++  ["inrun"]=>
++  object(Managed)#2 (1) {
++    ["owner"]=>
++    int(%s)
++  }
++}
++
+diff --git a/ext/pthreads/tests/display-startup-errors-output.phpt b/ext/pthreads/tests/display-startup-errors-output.phpt
+new file mode 100644
+index 00000000..51f7fa94
+--- /dev/null
++++ b/ext/pthreads/tests/display-startup-errors-output.phpt
+@@ -0,0 +1,43 @@
++--TEST--
++Tests that errors and uncaught exceptions on threads are correctly output
++--DESCRIPTION--
++Ensure errors and uncaught exceptions are properly displayed when display_startup_errors is not enabled (gh issue #761)
++--INI--
++display_startup_errors=0
++error_reporting=-1
++--FILE--
++<?php
++
++function throwException(){
++	throw new \Exception("Exception");
++}
++
++function undefined(){
++	if($a === true);
++}
++
++$w = new \Worker();
++$w->start();
++
++$w->stack(new class extends \Threaded{
++	public function run(){
++		throwException();
++	}
++});
++
++$w->stack(new class extends \Threaded{
++	public function run(){
++		undefined();
++	}
++});
++
++$w->shutdown();
++--EXPECTF--
++Fatal error: Uncaught Exception: Exception in %s:%d
++Stack trace:
++#0 %s(%d): throwException()
++#1 [internal function]: class@anonymous->run()
++#2 {main}
++  thrown in %s on line %d
++
++Notice: Undefined variable: a in %s on line %d
+diff --git a/ext/pthreads/tests/doc-comments.phpt b/ext/pthreads/tests/doc-comments.phpt
+new file mode 100644
+index 00000000..e403f703
+--- /dev/null
++++ b/ext/pthreads/tests/doc-comments.phpt
+@@ -0,0 +1,68 @@
++--TEST--
++Test doc comments are copied properly
++--DESCRIPTION--
++Test that doc comments are copied, no leaking/errors
++--FILE--
++<?php
++/**
++* Comment
++* @doc comment
++* @package package
++* @subpackage subpackage
++*/
++
++/**
++* Comment
++*/
++class T extends Thread {  
++    /**
++    * @var testing
++    */
++    public $content;
++    /**
++    * Method comment
++    * @doc comment
++    * @package package
++    * @subpackage subpackage
++    */
++    public function run() {
++       $reflect = new ReflectionMethod("T", "run");
++       var_dump($reflect);
++       var_dump($reflect->getDocComment());
++    }
++}
++
++$t = new T();
++$t->start();
++$t->join();
++
++$reflect = new ReflectionMethod("T", "run");
++var_dump($reflect);
++var_dump($reflect->getDocComment());
++
++?>
++--EXPECTF--
++object(ReflectionMethod)#2 (2) {
++  ["name"]=>
++  string(3) "run"
++  ["class"]=>
++  string(1) "T"
++}
++string(%d) "/**
++    * Method comment
++    * @doc comment
++    * @package package
++    * @subpackage subpackage
++    */"
++object(ReflectionMethod)#2 (2) {
++  ["name"]=>
++  string(3) "run"
++  ["class"]=>
++  string(1) "T"
++}
++string(%d) "/**
++    * Method comment
++    * @doc comment
++    * @package package
++    * @subpackage subpackage
++    */"
+diff --git a/ext/pthreads/tests/exception-handler-caught-nested.phpt b/ext/pthreads/tests/exception-handler-caught-nested.phpt
+new file mode 100644
+index 00000000..73753ede
+--- /dev/null
++++ b/ext/pthreads/tests/exception-handler-caught-nested.phpt
+@@ -0,0 +1,74 @@
++--TEST--
++Test exception handler (nested caught) bug #498
++--DESCRIPTION--
++More mishandling of exceptions, regression test
++--FILE--
++<?php
++class Test extends Thread {
++    public function run() {
++        set_exception_handler(function($message) {
++            var_dump('Uncaught', $message);
++        });
++
++        try {
++            self::processClassMethods();
++        } catch(\Throwable $e) {
++            var_dump('Caught', $e);
++        }
++    }
++
++    public static function processClassMethods() {
++        throw new \Exception();
++    }
++}
++$test = new Test();
++$test->start();
++$test->join();
++--EXPECTF--
++string(6) "Caught"
++object(Exception)#3 (7) {
++  ["message":protected]=>
++  string(0) ""
++  ["string":"Exception":private]=>
++  string(0) ""
++  ["code":protected]=>
++  int(0)
++  ["file":protected]=>
++  string(%d) "%s"
++  ["line":protected]=>
++  int(16)
++  ["trace":"Exception":private]=>
++  array(2) {
++    [0]=>
++    array(6) {
++      ["file"]=>
++      string(%d) "%s"
++      ["line"]=>
++      int(9)
++      ["function"]=>
++      string(19) "processClassMethods"
++      ["class"]=>
++      string(4) "Test"
++      ["type"]=>
++      string(2) "::"
++      ["args"]=>
++      array(0) {
++      }
++    }
++    [1]=>
++    array(4) {
++      ["function"]=>
++      string(3) "run"
++      ["class"]=>
++      string(4) "Test"
++      ["type"]=>
++      string(2) "->"
++      ["args"]=>
++      array(0) {
++      }
++    }
++  }
++  ["previous":"Exception":private]=>
++  NULL
++}
++
+diff --git a/ext/pthreads/tests/exception-handler-caught-uncaught.phpt b/ext/pthreads/tests/exception-handler-caught-uncaught.phpt
+new file mode 100644
+index 00000000..f6ece495
+--- /dev/null
++++ b/ext/pthreads/tests/exception-handler-caught-uncaught.phpt
+@@ -0,0 +1,89 @@
++--TEST--
++Test exception handler (caught and uncaught) bug #493
++--DESCRIPTION--
++Mishandling of exceptions caused catch() blocks to be ignored in some cases
++--FILE--
++<?php
++class Test extends Thread {
++
++    public function run() {
++		set_exception_handler(function($ex) {
++			var_dump('Uncaught', $ex);
++		});
++
++		try {
++			throw new \Exception();
++		} catch(Throwable $ex) {
++			var_dump('Caught', $ex);
++		}
++
++		throw new \Exception();
++    }
++}
++
++$test = new Test();
++$test->start();
++$test->join();
++--EXPECTF--
++string(6) "Caught"
++object(Exception)#%d (%d) {
++  ["message":protected]=>
++  string(0) ""
++  ["string":"Exception":private]=>
++  string(0) ""
++  ["code":protected]=>
++  int(0)
++  ["file":protected]=>
++  string(%d) "%s"
++  ["line":protected]=>
++  int(10)
++  ["trace":"Exception":private]=>
++  array(1) {
++    [0]=>
++    array(4) {
++      ["function"]=>
++      string(3) "run"
++      ["class"]=>
++      string(4) "Test"
++      ["type"]=>
++      string(2) "->"
++      ["args"]=>
++      array(0) {
++      }
++    }
++  }
++  ["previous":"Exception":private]=>
++  NULL
++}
++string(8) "Uncaught"
++object(Exception)#%d (%d) {
++  ["message":protected]=>
++  string(0) ""
++  ["string":"Exception":private]=>
++  string(0) ""
++  ["code":protected]=>
++  int(0)
++  ["file":protected]=>
++  string(%d) "%s"
++  ["line":protected]=>
++  int(15)
++  ["trace":"Exception":private]=>
++  array(1) {
++    [0]=>
++    array(4) {
++      ["function"]=>
++      string(3) "run"
++      ["class"]=>
++      string(4) "Test"
++      ["type"]=>
++      string(2) "->"
++      ["args"]=>
++      array(0) {
++      }
++    }
++  }
++  ["previous":"Exception":private]=>
++  NULL
++}
++
++
+diff --git a/ext/pthreads/tests/exception-handler-fail.phpt b/ext/pthreads/tests/exception-handler-fail.phpt
+new file mode 100644
+index 00000000..dc1a59cf
+--- /dev/null
++++ b/ext/pthreads/tests/exception-handler-fail.phpt
+@@ -0,0 +1,41 @@
++--TEST--
++Test exception handler inheritance (non threaded objects)
++--DESCRIPTION--
++Non-threaded objects caused pthreads up to v3.0.1 to fault when used as excepion handlers.
++
++Before we start a new thread, we apply a function to the exception handler stack that rebuilds their
++property tables.
++--FILE--
++<?php
++class ExceptionHandler
++{
++    public function handle(Throwable $e)
++    {
++        echo $e->getMessage();
++    }
++}
++
++class ExceptionThread extends Thread
++{
++	public function traceable() {
++		throw new Exception("OK");
++	}
++
++    public function run()
++    {
++        $this->traceable();
++    }
++}
++
++$handler = new ExceptionHandler();
++
++set_exception_handler([$handler, "handle"]);
++
++$t = new ExceptionThread();
++$t->start();
++$t->join();
++?>
++--EXPECT--
++OK
++
++
+diff --git a/ext/pthreads/tests/exception-handler-options.phpt b/ext/pthreads/tests/exception-handler-options.phpt
+new file mode 100644
+index 00000000..ee61efd5
+--- /dev/null
++++ b/ext/pthreads/tests/exception-handler-options.phpt
+@@ -0,0 +1,54 @@
++--TEST--
++Test exception handler inheritance (options)
++--DESCRIPTION--
++PTHREADS_INHERIT_CLASSES and PTHREADS_INHERIT_FUNCTIONS effect the handlers that can be copied.
++
++This test ensures that the various options are sensibly covered, and hopefully nothing crashes.
++--FILE--
++<?php
++class Handler {
++	public function handle(Throwable $ex) {
++		var_dump(__METHOD__, $ex->getMessage());
++	}
++}
++
++class Test extends Thread {
++	public function run () {
++		throw new Exception("oh dear");
++	}
++}
++
++$handler = new Handler();
++
++set_exception_handler([$handler, "handle"]);	# needs classes
++
++$test = new Test();
++$test->start(PTHREADS_INHERIT_FUNCTIONS);		# uncaught
++$test->join();
++
++$test = new Test();
++$test->start(PTHREADS_INHERIT_CLASSES);			# caught		
++$test->join();
++
++restore_exception_handler();
++
++set_exception_handler(function($ex){
++	var_dump(__FUNCTION__, $ex->getMessage());
++});
++
++$test = new Test();
++$test->start(PTHREADS_INHERIT_FUNCTIONS); 		# caught
++$test->join();
++?>
++--EXPECTF--
++Fatal error: Uncaught Exception: oh dear in %s:10
++Stack trace:
++#0 [internal function]: Test->run()
++#1 {main}
++  thrown in %s on line 10
++string(15) "Handler::handle"
++string(7) "oh dear"
++string(9) "{closure}"
++string(7) "oh dear"
++
++
+diff --git a/ext/pthreads/tests/exception-handler.phpt b/ext/pthreads/tests/exception-handler.phpt
+new file mode 100644
+index 00000000..eb97abf5
+--- /dev/null
++++ b/ext/pthreads/tests/exception-handler.phpt
+@@ -0,0 +1,79 @@
++--TEST--
++Test exception handler inheritance (ok)
++--DESCRIPTION--
++This test verifies that user exception handler is invoked by pthreads if set
++--FILE--
++<?php
++class ExceptionHandler
++{
++    static public function handle(Throwable $e)
++    {
++        var_dump($e);
++    }
++}
++
++class ExceptionThread extends Thread
++{
++	public function traceable() {
++		throw new Exception();
++	}
++    public function run()
++    {
++        $this->traceable();
++    }
++}
++
++/* this is now copied from parent, as you would expect it to be */
++set_exception_handler(array("ExceptionHandler", "handle"));
++
++$t = new ExceptionThread();
++$t->start();
++$t->join();
++?>
++--EXPECTF--
++object(Exception)#2 (%d) {
++  ["message":protected]=>
++  string(0) ""
++  ["string":"Exception":private]=>
++  string(0) ""
++  ["code":protected]=>
++  int(0)
++  ["file":protected]=>
++  string(%d) "%s"
++  ["line":protected]=>
++  int(13)
++  ["trace":"Exception":private]=>
++  array(2) {
++    [0]=>
++    array(6) {
++      ["file"]=>
++      string(%d) "%s"
++      ["line"]=>
++      int(17)
++      ["function"]=>
++      string(9) "traceable"
++      ["class"]=>
++      string(15) "ExceptionThread"
++      ["type"]=>
++      string(2) "->"
++      ["args"]=>
++      array(0) {
++      }
++    }
++    [1]=>
++    array(4) {
++      ["function"]=>
++      string(3) "run"
++      ["class"]=>
++      string(15) "ExceptionThread"
++      ["type"]=>
++      string(2) "->"
++      ["args"]=>
++      array(0) {
++      }
++    }
++  }
++  ["previous":"Exception":private]=>
++  NULL
++}
++
+diff --git a/ext/pthreads/tests/explicit-cast-arrays.phpt b/ext/pthreads/tests/explicit-cast-arrays.phpt
+new file mode 100644
+index 00000000..23666c9d
+--- /dev/null
++++ b/ext/pthreads/tests/explicit-cast-arrays.phpt
+@@ -0,0 +1,47 @@
++--TEST--
++Test Explicit Cast
++--DESCRIPTION--
++Normally, arrays are cast to Volatile objects when set as a member of a Threaded object.
++Casting the array as you are setting will override this behaviour, this is useful when passing an array out of a thread.
++For an example of that, see examples/ClosureFuture.php
++--FILE--
++<?php
++$threaded = new Threaded();
++
++$threaded->member = (array) [
++	"hello",
++	"world"
++];
++
++var_dump($threaded);
++
++$threaded->member = new Threaded;
++$threaded->member[0] = (array) [
++	"hello",
++	"world"
++];
++
++var_dump($threaded);
++?>
++--EXPECTF--
++object(Threaded)#%d (%d) {
++  ["member"]=>
++  array(2) {
++    [0]=>
++    string(5) "hello"
++    [1]=>
++    string(5) "world"
++  }
++}
++object(Threaded)#%d (%d) {
++  ["member"]=>
++  object(Threaded)#%d (%d) {
++    [0]=>
++    array(2) {
++      [0]=>
++      string(5) "hello"
++      [1]=>
++      string(5) "world"
++    }
++  }
++}
+diff --git a/ext/pthreads/tests/foreign-private-members.phpt b/ext/pthreads/tests/foreign-private-members.phpt
+new file mode 100644
+index 00000000..f44c01ba
+--- /dev/null
++++ b/ext/pthreads/tests/foreign-private-members.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Testing foreign private member access, magic methods bug #32
++--DESCRIPTION--
++Test that the fix for bug #32 is a success
++--FILE--
++<?php
++class MY {
++        private $test = "Hello World";
++
++        public function getTest(){
++                return $this->test;
++        }
++}
++
++class TEST extends Thread {
++        public function __construct($my) {
++                $this->my = $my;
++        }
++
++        public function run(){
++                printf("TEST: %s\n", $this->my->getTest());
++        }
++}
++
++$my = new MY();
++$test = new TEST($my);
++$test->start();
++--EXPECT--
++TEST: Hello World
++
++
+diff --git a/ext/pthreads/tests/functions.phpt b/ext/pthreads/tests/functions.phpt
+new file mode 100644
+index 00000000..32874df6
+--- /dev/null
++++ b/ext/pthreads/tests/functions.phpt
+@@ -0,0 +1,21 @@
++--TEST--
++Test function table inheritance
++--DESCRIPTION--
++This test verifies that the function table is inherited by pthreads correctly
++--FILE--
++<?php
++function TestFunction(){
++	return __FUNCTION__;
++}
++
++class TestThread extends Thread {
++	public function run() { 
++		printf("%s\n", TestFunction()); 
++	}
++}
++
++$thread = new TestThread();
++$thread->start();
++?>
++--EXPECT--
++TestFunction
+diff --git a/ext/pthreads/tests/global-constants.phpt b/ext/pthreads/tests/global-constants.phpt
+new file mode 100644
+index 00000000..c29c4210
+--- /dev/null
++++ b/ext/pthreads/tests/global-constants.phpt
+@@ -0,0 +1,36 @@
++--TEST--
++Test global constants
++--DESCRIPTION--
++This test verifies that global constants are inherited
++--FILE--
++<?php
++define ("SCONST", "mystring");
++define ("LCONST", 10);
++define ("DCONST", 1.19);
++define ("NCONST", null);
++define ("BCONST", true);
++
++class TestThread extends Thread {
++	public function run() {
++		foreach (array(
++			"string" => SCONST,
++			"long" => LCONST,
++			"double" => DCONST,
++			"null" => NCONST,
++			"boolean" => BCONST
++		) as $key => $constant) {
++			printf("%s:", $key);
++			var_dump($constant);
++		}
++	}
++}
++
++$thread = new TestThread();
++$thread->start();
++?>
++--EXPECT--
++string:string(8) "mystring"
++long:int(10)
++double:float(1.19)
++null:NULL
++boolean:bool(true)
+diff --git a/ext/pthreads/tests/gone.phpt b/ext/pthreads/tests/gone.phpt
+new file mode 100644
+index 00000000..2d310298
+--- /dev/null
++++ b/ext/pthreads/tests/gone.phpt
+@@ -0,0 +1,34 @@
++--TEST--
++Test objects that have gone away
++--DESCRIPTION--
++This test verifies that objects that have gone away do not cause segfaults
++--FILE--
++<?php
++class O extends Threaded { 
++	public function run() {
++
++	}
++}
++
++class T extends Thread {
++	public $o;
++
++	public function run() {
++		$this->o = new O();
++		/* this will disappear with this context */
++		$this->o["data"] = true;
++	}
++}
++
++$t = new T();
++$t->start();
++$t->join();
++
++var_dump($t->o);
++?>
++--EXPECTF--
++Fatal error: Uncaught %s: pthreads detected an attempt to connect to an object which has already been destroyed in %s:22
++Stack trace:
++#0 {main}
++  thrown in %s on line 22
++
+diff --git a/ext/pthreads/tests/graceful-fatalities.phpt b/ext/pthreads/tests/graceful-fatalities.phpt
+new file mode 100644
+index 00000000..5d967381
+--- /dev/null
++++ b/ext/pthreads/tests/graceful-fatalities.phpt
+@@ -0,0 +1,24 @@
++--TEST--
++Test graceful fatalities
++--DESCRIPTION--
++This test verifies that fatalities are graceful with regard to state
++--FILE--
++<?php
++class TestThread extends Thread {
++	public function run(){
++		/* silent fatal error */
++		echo @MY::$FATAL;
++	}
++}
++$test = new TestThread();
++$test->start();
++$test->join();
++var_dump($test->isTerminated());
++?>
++--EXPECTF--
++Fatal error: Uncaught Error: Class 'MY' not found in %s:5
++Stack trace:
++#0 [internal function]: TestThread->run()
++#1 {main}
++  thrown in %s on line 5
++bool(true)
+diff --git a/ext/pthreads/tests/hacking-serials.phpt b/ext/pthreads/tests/hacking-serials.phpt
+new file mode 100644
+index 00000000..53433b69
+--- /dev/null
++++ b/ext/pthreads/tests/hacking-serials.phpt
+@@ -0,0 +1,51 @@
++--TEST--
++Check graceful serialization of Threaded objects as members of non Threaded objects
++--DESCRIPTION--
++We don't need to serialize Threaded objects when they are set as members of other Threaded
++objects. However, a non-threaded object who has a Threaded object as a member must still serialize
++the Threaded object when that non-threaded object is set as a member of yet another Threaded object.
++
++So while Threaded objects can store other Threaded objects without hacky serialization routines, normal objects
++still require a hack.
++
++This test ensures the hack is right.
++--FILE--
++<?php
++class Wrapper {
++    public $worker;
++
++    public function __construct() {
++        $this->worker = new Worker;
++        $this->worker->start();
++    }
++
++    public function stack(Collectable $work) {
++        $this->worker->stack($work);
++    }
++
++	public function shutdown() {
++		$this->worker->shutdown();
++	}
++}
++
++class Work extends Threaded {
++    public $wrapper;
++    public function __construct(Wrapper $wrapper) {
++        $this->wrapper = $wrapper;
++    }
++
++    public function stack() {
++        $this->wrapper->stack($this);
++    }
++
++    public function run() {
++        echo "Foo\n";
++    }
++}
++$wrapper = new Wrapper;
++$work = new Work($wrapper);
++$work->stack();
++$wrapper->shutdown();
++
++--EXPECT--
++Foo
+diff --git a/ext/pthreads/tests/immutable-threaded-members.phpt b/ext/pthreads/tests/immutable-threaded-members.phpt
+new file mode 100644
+index 00000000..72bcdb7e
+--- /dev/null
++++ b/ext/pthreads/tests/immutable-threaded-members.phpt
+@@ -0,0 +1,86 @@
++--TEST--
++Test immutability
++--DESCRIPTION--
++To provide the best performance and and predictability, members of Threaded objects that are set to
++other Threaded objects are immutable, that is you cannot unset or write a member of a Threaded object
++if the member itself is a Threaded object.
++
++This test ensures that immutability works as intended with no strange side effects.
++--FILE--
++<?php
++class T extends Thread {
++
++	public function __construct() {
++		$this->t = new Threaded(); 	# now immutable
++		$this->t->set = true; 		# fine
++		$this->s = new stdClass(); 	# fine, not immutable
++	}
++	
++    public function run(){
++		$this->synchronized(function(){
++			while (!$this->printed) {
++				$this->wait(); 			# wait for object to be printed in main context
++			}
++		});
++		
++		try {
++			$this->t = new Threaded(); 	# can't do it, immutable member
++		} catch(Exception $ex) {
++			printf("%s in %s on line %d\n", $ex->getMessage(), $ex->getFile(), $ex->getLine());
++		}
++
++		try {
++			unset($this->t); 			# can't do it, immutable member
++		} catch(Exception $ex) {
++			printf("%s in %s on line %d\n", $ex->getMessage(), $ex->getFile(), $ex->getLine());
++		}
++	}
++}
++
++$t = new T();
++var_dump($t->t, $t->s);
++$t->start();
++
++$t->synchronized(function() use($t) {
++	var_dump($t->t, $t->s);
++	$t->printed = true;	
++	$t->notify();
++});
++
++$t->join();
++
++try {
++	$t->t = new Threaded(); 		# can't do it, immutable member
++} catch(Exception $ex) {
++	printf("%s in %s on line %d\n", $ex->getMessage(), $ex->getFile(), $ex->getLine());
++}
++
++try {
++	unset($t->t);					# can't do it, immutable member
++} catch(Exception $ex) {
++	printf("%s in %s on line %d\n", $ex->getMessage(), $ex->getFile(), $ex->getLine());
++}
++
++var_dump($t->t);					# not mutated
++--EXPECTF--
++object(Threaded)#%d (%d) {
++  ["set"]=>
++  bool(true)
++}
++object(stdClass)#%d (%d) {
++}
++object(Threaded)#%d (%d) {
++  ["set"]=>
++  bool(true)
++}
++object(stdClass)#%d (%d) {
++}
++Threaded members previously set to Threaded objects are immutable, cannot overwrite t in %s on line 18
++Threaded members previously set to Threaded objects are immutable, cannot overwrite t in %s on line 24
++Threaded members previously set to Threaded objects are immutable, cannot overwrite t in %s on line 44
++Threaded members previously set to Threaded objects are immutable, cannot overwrite t in %s on line 50
++object(Threaded)#%d (%d) {
++  ["set"]=>
++  bool(true)
++}
++
+diff --git a/ext/pthreads/tests/include_interface_constant.phpt b/ext/pthreads/tests/include_interface_constant.phpt
+new file mode 100644
+index 00000000..7c6b0383
+--- /dev/null
++++ b/ext/pthreads/tests/include_interface_constant.phpt
+@@ -0,0 +1,26 @@
++--TEST--
++Constants copying of an included interface
++--DESCRIPTION--
++We need to copy constants which are defined by a via autoloader included interface
++--FILE--
++<?php
++
++class AutoLoader {
++    public function __construct() {
++        spl_autoload_register(array($this, 'loadClass'));
++    }
++
++    private function loadClass() {
++        require __DIR__ .'/assets/ExternalConstantsInterface.php';
++    }
++}
++new AutoLoader();
++
++class MyClass implements ExternalConstantsInterface {}
++
++var_dump(MyClass::BUZZ);
++
++$thread = new Thread();
++$thread->start(PTHREADS_INHERIT_ALL) && $thread->join();
++--EXPECT--
++int(1)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/included-files.phpt b/ext/pthreads/tests/included-files.phpt
+new file mode 100644
+index 00000000..fdd27637
+--- /dev/null
++++ b/ext/pthreads/tests/included-files.phpt
+@@ -0,0 +1,22 @@
++--TEST--
++Test include/require functions as expected
++--DESCRIPTION--
++This test verifies that require_once and include are working as expected
++--FILE--
++<?php
++define("INC", sprintf("%s/includeme.inc", dirname(__FILE__)));
++
++include(INC);
++class TestThread extends Thread {
++	public function run(){
++		require_once(INC);
++		if (!function_exists("myTestFunc")) {
++			printf("FAILED\n");
++		} else printf("OK\n");
++	}
++}
++$test = new TestThread();
++$test->start();
++?>
++--EXPECT--
++OK
+diff --git a/ext/pthreads/tests/includeme.inc b/ext/pthreads/tests/includeme.inc
+new file mode 100644
+index 00000000..e507b4d0
+--- /dev/null
++++ b/ext/pthreads/tests/includeme.inc
+@@ -0,0 +1,2 @@
++<?php
++function myTestFunc(){}
+diff --git a/ext/pthreads/tests/inherit-non-closures.phpt b/ext/pthreads/tests/inherit-non-closures.phpt
+new file mode 100644
+index 00000000..462ac2b7
+--- /dev/null
++++ b/ext/pthreads/tests/inherit-non-closures.phpt
+@@ -0,0 +1,19 @@
++--TEST--
++Test inherit none closures
++--DESCRIPTION--
++This test verifies that closures work when using PTHREADS_INHERIT_NONE
++--FILE--
++<?php
++class Test extends Thread {
++	public function run() {
++		$this->synchronized(function(){
++			echo "OK\n";
++		});
++	}
++}
++
++$test = new Test();
++$test->start(PTHREADS_INHERIT_NONE);
++$test->join();
++--EXPECT--
++OK
+diff --git a/ext/pthreads/tests/inherited-anon-class-outside-context.phpt b/ext/pthreads/tests/inherited-anon-class-outside-context.phpt
+new file mode 100644
+index 00000000..c60bdaf8
+--- /dev/null
++++ b/ext/pthreads/tests/inherited-anon-class-outside-context.phpt
+@@ -0,0 +1,73 @@
++--TEST--
++Basic anonymous class support, fix #505
++--DESCRIPTION--
++Unbound anon class causing segfaults, we delay copy but still cannot serialize the anon
++--FILE--
++<?php
++class Test extends Thread {
++	/**
++	 * doccomment run
++	 */
++	public function run() {
++		$this->alive = true;
++		/**
++		 * doccomment anonymous
++		 */
++		$this->anonymous = new class extends Thread {
++			const CONSTANT = 'constant';
++			/**
++			 * @var
++			 */
++			public $pubProp;
++			protected $protProp;
++			private $privProp;
++			public static $staticProp;
++			public function run() {
++				var_dump('anonymous run');
++				$this->ready = true;
++			}
++			public function method() {
++				var_dump('method executed');
++			}
++			public static function staticMethod() {}
++		};
++		var_dump($this->anonymous);
++		$this->anonymous->start();
++		$this->anonymous->join();
++		while($this->alive) {}
++	}
++}
++$test = new Test();
++$test->start();
++while(true) {
++	if(isset($test->anonymous, $test->anonymous->ready)) {
++		var_dump($test->anonymous);
++		$test->anonymous->run();
++		$test->anonymous->method();
++		$test->alive = false;
++		break;
++	}
++}
++$test->join();
++--EXPECT--
++object(class@anonymous)#2 (3) {
++  ["pubProp"]=>
++  NULL
++  ["protProp"]=>
++  NULL
++  ["privProp"]=>
++  NULL
++}
++string(13) "anonymous run"
++object(class@anonymous)#2 (4) {
++  ["pubProp"]=>
++  NULL
++  ["protProp"]=>
++  NULL
++  ["privProp"]=>
++  NULL
++  ["ready"]=>
++  bool(true)
++}
++string(13) "anonymous run"
++string(15) "method executed"
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/inherited-anon-class.phpt b/ext/pthreads/tests/inherited-anon-class.phpt
+new file mode 100644
+index 00000000..14c3ff4a
+--- /dev/null
++++ b/ext/pthreads/tests/inherited-anon-class.phpt
+@@ -0,0 +1,17 @@
++--TEST--
++Test fix for #658 with inheritance
++--DESCRIPTION--
++Unbound anon class causing segfaults, we delay copy but still cannot serialize the anon
++--FILE--
++<?php
++$task = new class extends Thread {
++    public function run()
++    {
++        $this->prop = new class extends Threaded {};
++		var_dump($this->prop);
++    }
++};
++$task->start() && $task->join();
++--EXPECT--
++object(class@anonymous)#2 (0) {
++}
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/ini.phpt b/ext/pthreads/tests/ini.phpt
+new file mode 100644
+index 00000000..a1d3da34
+--- /dev/null
++++ b/ext/pthreads/tests/ini.phpt
+@@ -0,0 +1,17 @@
++--TEST--
++Test INI inheritance (bug 20)
++--DESCRIPTION--
++This test will ensure that INI directives are inherited and or created upon initialization of new threads
++--FILE--
++<?php
++ini_set("include_path", ":/var/lib/other");
++class Test extends Thread {
++	public function run(){
++		printf("%s: %s\n", __METHOD__, ini_get("include_path"));
++	}
++}
++$test = new Test();
++$test->start();
++?>
++--EXPECT--
++Test::run: :/var/lib/other
+diff --git a/ext/pthreads/tests/interface.phpt b/ext/pthreads/tests/interface.phpt
+new file mode 100644
+index 00000000..7d33c303
+--- /dev/null
++++ b/ext/pthreads/tests/interface.phpt
+@@ -0,0 +1,28 @@
++--TEST--
++Test interface functionality
++--DESCRIPTION--
++This test verifies that interfaces are handled properly by pthreads
++--FILE--
++<?php
++interface INamedThread {
++	function setName($name);
++	function getName();
++}
++
++class TestThread extends Thread implements INamedThread {
++	public function setName($name) {
++		$this->name = $name;
++	}
++	public function getName() {
++		return $this->name;
++	}
++	
++	public function run() { printf("%s\n", $this->getName()); }
++}
++
++$thread = new TestThread();
++$thread->setName("InterfaceTest");
++$thread->start();
++?>
++--EXPECT--
++InterfaceTest
+diff --git a/ext/pthreads/tests/interfaces.phpt b/ext/pthreads/tests/interfaces.phpt
+new file mode 100644
+index 00000000..6dbbd083
+--- /dev/null
++++ b/ext/pthreads/tests/interfaces.phpt
+@@ -0,0 +1,36 @@
++--TEST--
++Testing interfaces inheritance (#124)
++--DESCRIPTION--
++Test if interfaces are stored in thread
++--FILE--
++<?php
++interface iMY{
++    function getTest();
++}
++
++class MY implements iMY{
++    private $test = "Hello World";
++
++    public function getTest(){
++            return $this->test;
++    }
++}
++
++class TEST extends Thread {
++    public function __construct() {
++
++    }
++
++    public function run(){
++	    $MY = new MY();
++        var_dump(in_array("iMY",get_declared_interfaces()));
++	    var_dump(in_array("iMY",class_implements($MY)));
++    }
++}
++
++$test = new TEST();
++$test->start();
++?>
++--EXPECT--
++bool(true)
++bool(true)
+diff --git a/ext/pthreads/tests/ipv6_skipif.inc b/ext/pthreads/tests/ipv6_skipif.inc
+new file mode 100644
+index 00000000..fa8b1da7
+--- /dev/null
++++ b/ext/pthreads/tests/ipv6_skipif.inc
+@@ -0,0 +1,6 @@
++<?php
++if (!defined("\Socket::AF_INET6")) {
++	die('skip no IPv6 support');
++}
++if (@stream_socket_client('udp://[::1]:8888') === false)
++	die('skip no IPv6 support');
+diff --git a/ext/pthreads/tests/isset-empty-compat.phpt b/ext/pthreads/tests/isset-empty-compat.phpt
+new file mode 100644
+index 00000000..1ac5f084
+--- /dev/null
++++ b/ext/pthreads/tests/isset-empty-compat.phpt
+@@ -0,0 +1,93 @@
++--TEST--
++Test isset and empty are compliant with standard objects (fix bug #347)
++--FILE--
++<?php
++
++class standard {
++    public $t_false = false;
++    public $t_null = null;
++    public $t_emptyStr = "";
++    public $t_zero1 = 0;
++    public $t_zero2 = 0.0;
++    public $t_zero3 = "0";
++    public $t_notSet;
++    public $t_notEmpty = true;
++}
++
++class threadedObject extends \Threaded {
++    public $t_false = false;
++    public $t_null = null;
++    public $t_emptyStr = "";
++    public $t_zero1 = 0;
++    public $t_zero2 = 0.0;
++    public $t_zero3 = "0";
++    public $t_notSet;
++    public $t_notEmpty = true;
++}
++
++$stdObject = new standard;
++$stdObject->aEmpty = [];
++$stdObject->aNotEmpty = [1];
++
++$threaded = new threadedObject;
++$threaded->aEmpty = (array) [];
++$threaded->aNotEmpty = (array) [1];
++
++foreach (array(
++    't_false',
++    't_null',
++    't_emptyStr',
++    't_zero1',
++    't_zero2',
++    't_zero3',
++    't_notSet',
++    't_notEmpty',
++	'aEmpty',
++	'aNotEmpty') as $prop) {
++    
++    printf("%s:\n", $prop);
++    var_dump(isset($stdObject->$prop) == isset($threaded->$prop));
++    var_dump(empty($stdObject->$prop) == empty($threaded->$prop));
++    printf("\n");
++}
++
++--EXPECT--
++t_false:
++bool(true)
++bool(true)
++
++t_null:
++bool(true)
++bool(true)
++
++t_emptyStr:
++bool(true)
++bool(true)
++
++t_zero1:
++bool(true)
++bool(true)
++
++t_zero2:
++bool(true)
++bool(true)
++
++t_zero3:
++bool(true)
++bool(true)
++
++t_notSet:
++bool(true)
++bool(true)
++
++t_notEmpty:
++bool(true)
++bool(true)
++
++aEmpty:
++bool(true)
++bool(true)
++
++aNotEmpty:
++bool(true)
++bool(true)
+diff --git a/ext/pthreads/tests/isset-unset.phpt b/ext/pthreads/tests/isset-unset.phpt
+new file mode 100644
+index 00000000..07947d1a
+--- /dev/null
++++ b/ext/pthreads/tests/isset-unset.phpt
+@@ -0,0 +1,86 @@
++--TEST--
++Testing isset unset
++--DESCRIPTION--
++This tests that isset and unset are working properly
++--FILE--
++<?php
++class Storage extends Threaded {
++    public function run(){}
++}
++
++$storage = new Storage();
++$storage->merge(range(1, 10));
++var_dump($storage);
++
++for($i = 0; $i < 10; $i ++) {
++    var_dump(isset($storage[$i]));
++    var_dump($storage[$i]);
++    unset($storage[$i]);
++    var_dump(isset($storage[$i]));
++    printf("\n");
++}
++?>
++--EXPECT--
++object(Storage)#1 (10) {
++  [0]=>
++  int(1)
++  [1]=>
++  int(2)
++  [2]=>
++  int(3)
++  [3]=>
++  int(4)
++  [4]=>
++  int(5)
++  [5]=>
++  int(6)
++  [6]=>
++  int(7)
++  [7]=>
++  int(8)
++  [8]=>
++  int(9)
++  [9]=>
++  int(10)
++}
++bool(true)
++int(1)
++bool(false)
++
++bool(true)
++int(2)
++bool(false)
++
++bool(true)
++int(3)
++bool(false)
++
++bool(true)
++int(4)
++bool(false)
++
++bool(true)
++int(5)
++bool(false)
++
++bool(true)
++int(6)
++bool(false)
++
++bool(true)
++int(7)
++bool(false)
++
++bool(true)
++int(8)
++bool(false)
++
++bool(true)
++int(9)
++bool(false)
++
++bool(true)
++int(10)
++bool(false)
++
++
+diff --git a/ext/pthreads/tests/iterator-basic.phpt b/ext/pthreads/tests/iterator-basic.phpt
+new file mode 100644
+index 00000000..67009ce9
+--- /dev/null
++++ b/ext/pthreads/tests/iterator-basic.phpt
+@@ -0,0 +1,34 @@
++--TEST--
++Test iterating on Threaded
++--DESCRIPTION--
++Regression test for bugs introduced with Threaded iteration in PHP 7.3
++--FILE--
++<?php
++$threaded = new Threaded();
++
++var_dump($threaded->count());
++foreach($threaded as $k => $prop){
++	var_dump("should not happen");
++}
++
++for($i = 0; $i < 5; ++$i){
++	$threaded[] = "value$i";
++}
++
++foreach($threaded as $i => $prop){
++	var_dump($i, $prop);
++}
++?>
++--EXPECTF--
++int(0)
++int(0)
++string(6) "value0"
++int(1)
++string(6) "value1"
++int(2)
++string(6) "value2"
++int(3)
++string(6) "value3"
++int(4)
++string(6) "value4"
++
+diff --git a/ext/pthreads/tests/iterator-funcs.phpt b/ext/pthreads/tests/iterator-funcs.phpt
+new file mode 100644
+index 00000000..90931211
+--- /dev/null
++++ b/ext/pthreads/tests/iterator-funcs.phpt
+@@ -0,0 +1,54 @@
++--TEST--
++Testing iterator functions
++--DESCRIPTION--
++Iterator functions were not properly copied, causing unexpected results and unsafe execution.
++--FILE--
++<?php
++class MyIterator implements \Iterator { 
++        protected $items;
++        protected $position;
++
++        public function __construct() {
++                $this->items = array("joe", "bob", "fred");
++                $this->position = 0;
++        }
++
++        public function next() {
++                ++$this->position;
++        }
++
++        public function key() {
++                return $this->position;
++        }
++
++        public function current() {
++                return $this->items[$this->position];
++        }
++
++        public function valid() {
++                return ($this->position < count($this->items));
++        }
++
++        public function rewind() {
++                $this->myProtectedMethod();
++                $this->position = 0;
++        }
++
++        protected function myProtectedMethod() {}
++}
++
++class MyThread extends \Thread {
++        public function run() {
++                $it = new \MyIterator();
++                foreach ($it as $item) {}
++                print "SUCCESS";
++        }
++}
++
++$items = new \MyIterator();
++foreach ($items as $item) {}
++$thread = new \MyThread();
++$thread->start();
++$thread->join();
++--EXPECT--
++SUCCESS
+diff --git a/ext/pthreads/tests/iterator-no-refs.phpt b/ext/pthreads/tests/iterator-no-refs.phpt
+new file mode 100644
+index 00000000..43120965
+--- /dev/null
++++ b/ext/pthreads/tests/iterator-no-refs.phpt
+@@ -0,0 +1,15 @@
++--TEST--
++Test disallowing iteration by reference
++--DESCRIPTION--
++We cannot supply a reference to properties, as modifying them would not have the desired result
++To avoid surprises, we throw an exception ...
++--FILE--
++<?php
++foreach (new Threaded() as $k => &$v) {}
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: iteration by reference is not allowed on Threaded objects in %s:2
++Stack trace:
++#0 {main}
++  thrown in %s on line 2
++
+diff --git a/ext/pthreads/tests/legacy-ctor.phpt b/ext/pthreads/tests/legacy-ctor.phpt
+new file mode 100644
+index 00000000..e2ca0cd8
+--- /dev/null
++++ b/ext/pthreads/tests/legacy-ctor.phpt
+@@ -0,0 +1,24 @@
++--TEST--
++Test legacy constructor (issue #336)
++--DESCRIPTION--
++This test verifies that legacy ctors do not induce failure
++--FILE--
++<?php
++class test2 {
++        function test2() {
++            echo "ctor test2\n";
++        }
++}
++
++class test1 extends Thread {
++        function run() {
++                $x = new test2();
++        }
++}
++
++$t = new test1();
++$t->start();
++$t->join();
++--EXPECTF--
++Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; test2 has a deprecated constructor in %s on line %d
++ctor test2
+diff --git a/ext/pthreads/tests/lexical-vars.phpt b/ext/pthreads/tests/lexical-vars.phpt
+new file mode 100644
+index 00000000..7b93e5bd
+--- /dev/null
++++ b/ext/pthreads/tests/lexical-vars.phpt
+@@ -0,0 +1,69 @@
++--TEST--
++Test lexical vars
++--DESCRIPTION--
++In pthreads v2, it was difficult to support lexical or function-static
++scope vars, for various reasons.
++
++In pthreads v3, it is now possible to have some support, the following things will work
++	Threaded objects
++	Closures
++	Arrays
++	Scalars
++
++The following things will not work:
++	Non-threaded objects
++	Resources
++
++Unsupported types should not raise notices, they will simply be undefined in the function scope.
++--FILE--
++<?php
++class TestThread extends \Thread
++{
++    private $function;
++    public function __construct(callable $function)
++    {
++        $this->function = $function;
++    }
++    public function run()
++    {
++        $function = $this->function;
++        $function();
++    }
++}
++
++$scalar = 1; # will work
++$string = "thing"; # will work
++$res = fopen("php://stdin", "r"); # won't work
++$threaded = new Threaded(); # will work
++$closure = function() { # will work
++	echo "hi";
++};
++$array = [1, 2, 3, 4, 5]; # will work
++$std = new stdClass; # won't work
++
++$thread = new TestThread(function () use ($scalar, $string, $res, $threaded, $closure, $array, $std) {
++    var_dump($scalar, $string, $res, $threaded, $closure, $array, $std);
++});
++$thread->start();
++--EXPECTF--
++int(1)
++string(5) "thing"
++NULL
++object(Threaded)#2 (0) {
++}
++object(Closure)#3 (0) {
++}
++array(5) {
++  [0]=>
++  int(1)
++  [1]=>
++  int(2)
++  [2]=>
++  int(3)
++  [3]=>
++  int(4)
++  [4]=>
++  int(5)
++}
++NULL
++
+diff --git a/ext/pthreads/tests/magic-methods-get-set.phpt b/ext/pthreads/tests/magic-methods-get-set.phpt
+new file mode 100644
+index 00000000..ca5468bc
+--- /dev/null
++++ b/ext/pthreads/tests/magic-methods-get-set.phpt
+@@ -0,0 +1,30 @@
++--TEST--
++Test magic __get and __set
++--DESCRIPTION--
++This test verifies that __set and __get work as expected
++--FILE--
++<?php
++class Test extends Threaded {
++
++    public function __get($key) {
++        printf(
++            "magic %s\n", __FUNCTION__);
++        return $this[$key];
++    }
++    
++    public function __set($key, $value) {
++        printf(
++            "magic %s\n", __FUNCTION__);
++        return $this[$key] = $value;
++    }
++}
++
++$test = new Test();
++$test->one = "one";
++var_dump($test->one);
++
++?>
++--EXPECT--
++magic __set
++magic __get
++string(3) "one"
+diff --git a/ext/pthreads/tests/magic-methods-isset-unset.phpt b/ext/pthreads/tests/magic-methods-isset-unset.phpt
+new file mode 100644
+index 00000000..929b69ca
+--- /dev/null
++++ b/ext/pthreads/tests/magic-methods-isset-unset.phpt
+@@ -0,0 +1,32 @@
++--TEST--
++Test magic __isset and __unset
++--DESCRIPTION--
++This test verifies that __isset and __unset work as expected
++--FILE--
++<?php
++class Test extends Threaded {
++
++    public function __isset($key) {
++        printf("magic %s\n", __FUNCTION__);
++        
++        return isset($this[$key]);
++    }
++    
++    public function __unset($key) {
++        printf("magic %s\n", __FUNCTION__);
++        
++        unset($this[$key]);
++    }
++}
++$test = new Test();
++$test->one = 1;
++var_dump(isset($test->one));
++unset($test->one);
++var_dump(isset($test->one));
++?>
++--EXPECT--
++magic __isset
++bool(true)
++magic __unset
++magic __isset
++bool(false)
+diff --git a/ext/pthreads/tests/merge-non-threaded.phpt b/ext/pthreads/tests/merge-non-threaded.phpt
+new file mode 100644
+index 00000000..58f7d7d1
+--- /dev/null
++++ b/ext/pthreads/tests/merge-non-threaded.phpt
+@@ -0,0 +1,53 @@
++--TEST--
++Test merge non-threaded
++--DESCRIPTION--
++This test verifies that merging with non-threaded classes works as expected
++--FILE--
++<?php
++$array = [];
++
++while (count($array) < 10)
++    $array[] = count($array);
++
++$stdClass = new stdClass();
++$stdClass->foo = "foo";
++$stdClass->bar = "bar";
++$stdClass->baz = "baz";
++
++$safe = new Threaded();
++$safe->merge($array);
++
++$safe->foo = "bar";
++$safe->merge($stdClass, false);
++
++var_dump($safe);
++?>
++--EXPECTF--
++object(Threaded)#%d (%d) {
++  [0]=>
++  int(0)
++  [1]=>
++  int(1)
++  [2]=>
++  int(2)
++  [3]=>
++  int(3)
++  [4]=>
++  int(4)
++  [5]=>
++  int(5)
++  [6]=>
++  int(6)
++  [7]=>
++  int(7)
++  [8]=>
++  int(8)
++  [9]=>
++  int(9)
++  ["foo"]=>
++  string(3) "bar"
++  ["bar"]=>
++  string(3) "bar"
++  ["baz"]=>
++  string(3) "baz"
++}
+diff --git a/ext/pthreads/tests/merging-members.phpt b/ext/pthreads/tests/merging-members.phpt
+new file mode 100644
+index 00000000..e10c74ad
+--- /dev/null
++++ b/ext/pthreads/tests/merging-members.phpt
+@@ -0,0 +1,48 @@
++--TEST--
++Testing merging members
++--DESCRIPTION--
++This tests that merging members works as expected
++--FILE--
++<?php
++class Store extends Threaded {
++        public function run(){}
++}
++
++$stores = array(new Store(), new Store());
++
++$stores[0]->test = "one";
++$stores[1]->other = "when";
++$stores[1]->test = "two";
++$stores[1]->next = 0.01;
++
++$stores[0]->merge($stores[1]);
++
++var_dump($stores[0]);
++
++$stores[0]->test = "one";
++$stores[1]->other = "when";
++$stores[1]->test = "two";
++$stores[1]->next = 0.01;
++
++$stores[0]->merge($stores[1], false);
++
++var_dump($stores[0]);
++?>
++--EXPECT--
++object(Store)#1 (3) {
++  ["test"]=>
++  string(3) "two"
++  ["other"]=>
++  string(4) "when"
++  ["next"]=>
++  float(0.01)
++}
++object(Store)#1 (3) {
++  ["test"]=>
++  string(3) "one"
++  ["other"]=>
++  string(4) "when"
++  ["next"]=>
++  float(0.01)
++}
++
+diff --git a/ext/pthreads/tests/merging-range.phpt b/ext/pthreads/tests/merging-range.phpt
+new file mode 100644
+index 00000000..e9b0dc4d
+--- /dev/null
++++ b/ext/pthreads/tests/merging-range.phpt
+@@ -0,0 +1,16 @@
++--TEST--
++Testing merging members (long keys)
++--DESCRIPTION--
++This tests that merging ranges works as expected (long keys)
++--FILE--
++<?php
++class Storage extends Threaded {
++    public function run() {}
++}
++
++$storage = new Storage();
++$storage->merge(range("0", "3"));
++var_dump(count($storage));
++?>
++--EXPECTF--
++int(4)
+diff --git a/ext/pthreads/tests/nested-non-threaded-object-references.phpt b/ext/pthreads/tests/nested-non-threaded-object-references.phpt
+new file mode 100644
+index 00000000..956923b4
+--- /dev/null
++++ b/ext/pthreads/tests/nested-non-threaded-object-references.phpt
+@@ -0,0 +1,28 @@
++--TEST--
++Test nested non-threaded object references
++--DESCRIPTION--
++This test verifies the possibility to nest non-threaded object references
++--FILE--
++<?php
++for ($i = 0; $i < 10; ++$i) {
++    $a = new \stdClass();
++    $a->b = [];
++    for($j = 0; $j < 5; ++$j){
++        $obj = new \stdClass();
++        $obj->a = $a;
++        $a->b[] = $obj;
++    }
++    new class($obj) extends \Threaded {
++        public function __construct($faction) {
++            $this->fraction = $faction;
++
++            for ($i = 0; $i < 1000; ++$i) {
++                $this->fraction;
++            }
++        }
++    };
++}
++var_dump("ok");
++?>
++--EXPECT--
++string(2) "ok"
+diff --git a/ext/pthreads/tests/new-class-after-thread-creation.phpt b/ext/pthreads/tests/new-class-after-thread-creation.phpt
+new file mode 100644
+index 00000000..ccf71a7d
+--- /dev/null
++++ b/ext/pthreads/tests/new-class-after-thread-creation.phpt
+@@ -0,0 +1,18 @@
++--TEST--
++New class after thread creation
++--DESCRIPTION--
++Fixes bug #711. This requires a class to be declared after a new thread has been
++created, where the new class implements at least one interface
++--FILE--
++<?php
++
++$worker = new \Worker();
++$worker->start();
++
++interface A {}
++class task extends Threaded implements A {}
++
++$worker->stack(new task());
++while($worker->collect());
++$worker->shutdown();
++--EXPECT--
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/no-cloning.phpt b/ext/pthreads/tests/no-cloning.phpt
+new file mode 100644
+index 00000000..edf61b5e
+--- /dev/null
++++ b/ext/pthreads/tests/no-cloning.phpt
+@@ -0,0 +1,16 @@
++--TEST--
++Test no cloning
++--DESCRIPTION--
++This test that clone will not operate (or have strange side effects)
++--FILE--
++<?php
++$threaded = new Threaded();
++$clone = clone $threaded;
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: Threaded objects cannot be cloned in %s:3
++Stack trace:
++#0 {main}
++  thrown in %s on line 3
++
++
+diff --git a/ext/pthreads/tests/no-inheritance-anon-class.phpt b/ext/pthreads/tests/no-inheritance-anon-class.phpt
+new file mode 100644
+index 00000000..19adbc6e
+--- /dev/null
++++ b/ext/pthreads/tests/no-inheritance-anon-class.phpt
+@@ -0,0 +1,16 @@
++--TEST--
++Test fix for #659
++--DESCRIPTION--
++Unbound anon class causing segfaults, we delay copy but still cannot serialize the anon
++--FILE--
++<?php
++$task = new class extends Thread {
++    public function run()
++    {
++        $this->prop = new class {};
++		var_dump($this->prop); /* we do expect null: anon classes cannot be serialized */
++    }
++};
++$task->start() && $task->join();
++--EXPECT--
++NULL
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/norefs.phpt b/ext/pthreads/tests/norefs.phpt
+new file mode 100644
+index 00000000..326b48b1
+--- /dev/null
++++ b/ext/pthreads/tests/norefs.phpt
+@@ -0,0 +1,57 @@
++--TEST--
++Test members (typeof object) with no other references
++--DESCRIPTION--
++In pthreads v2, you were charged with responsability of the objects you create, however in pthreads v3, a Threaded object
++is able to retain a reference to another Threaded object, this makes programming Thread objects a lot simpler, 
++the code in this test would fail with pthreads v2 and will work as expected with pthreads v3, which is a win for everyone ...
++--FILE--
++<?php
++class O extends Threaded {
++	public function run(){}
++}
++
++class T extends Thread {
++
++	public function __construct() {
++		$this->t = new O();
++		$this->t->set = true;
++		$this->s = new stdClass();
++	}
++	
++    public function run(){
++		var_dump($this->t, $this->s, $this);
++	}
++}
++
++$t = new T();
++var_dump($t);
++$t->start();
++$t->join();
++?>
++--EXPECTF--
++object(T)#%d (%d) {
++  ["t"]=>
++  object(O)#%d (%d) {
++    ["set"]=>
++    bool(true)
++  }
++  ["s"]=>
++  object(stdClass)#%d (%d) {
++  }
++}
++object(O)#2 (%d) {
++  ["set"]=>
++  bool(true)
++}
++object(stdClass)#%d (%d) {
++}
++object(T)#%d (%d) {
++  ["t"]=>
++  object(O)#%d (%d) {
++    ["set"]=>
++    bool(true)
++  }
++  ["s"]=>
++  object(stdClass)#%d (%d) {
++  }
++}
+diff --git a/ext/pthreads/tests/normal-reads.phpt b/ext/pthreads/tests/normal-reads.phpt
+new file mode 100644
+index 00000000..81d280cb
+--- /dev/null
++++ b/ext/pthreads/tests/normal-reads.phpt
+@@ -0,0 +1,20 @@
++--TEST--
++Test reading object properties without debug info
++--DESCRIPTION--
++This test verifies that reading properties from the object without var_dump/print_r will work
++--FILE--
++<?php
++class Test extends Thread {
++	public function run() { 
++		$this->name = sprintf("%s", __CLASS__);
++	}
++}
++
++$thread = new Test();
++$thread->start();
++$thread->join();
++foreach($thread as $key => $value)
++	var_dump($value);
++?>
++--EXPECT--
++string(4) "Test"
+diff --git a/ext/pthreads/tests/normalize-members.phpt b/ext/pthreads/tests/normalize-members.phpt
+new file mode 100644
+index 00000000..e61bbe92
+--- /dev/null
++++ b/ext/pthreads/tests/normalize-members.phpt
+@@ -0,0 +1,22 @@
++--TEST--
++Testing normalizing members
++--DESCRIPTION--
++This tests that normalizing members works without effort
++--FILE--
++<?php
++class Test extends Threaded {
++	public function run() { 
++	}
++}
++
++$t = new Test();
++$t[] = "one";
++$t[] = "two";
++$t["three"] = "three";
++
++/* get a normal array */
++$normal = (array) $t;
++var_dump(is_array($normal));
++?>
++--EXPECT--
++bool(true)
+diff --git a/ext/pthreads/tests/null-member-crash.phpt b/ext/pthreads/tests/null-member-crash.phpt
+new file mode 100644
+index 00000000..d6eb879b
+--- /dev/null
++++ b/ext/pthreads/tests/null-member-crash.phpt
+@@ -0,0 +1,18 @@
++--TEST--
++Null member crash
++--DESCRIPTION--
++This test verifies that null members do not crash php
++--FILE--
++<?php
++class Test extends Threaded {
++    public function run(){}
++}
++$test = new Test();
++@$test[$undefined]="what";
++var_dump($test);
++?>
++--EXPECTF--
++object(Test)#1 (1) {
++  [0]=>
++  string(4) "what"
++}
+diff --git a/ext/pthreads/tests/object-cast.phpt b/ext/pthreads/tests/object-cast.phpt
+new file mode 100644
+index 00000000..0a60d562
+--- /dev/null
++++ b/ext/pthreads/tests/object-cast.phpt
+@@ -0,0 +1,50 @@
++--TEST--
++Test object cast
++--DESCRIPTION--
++If you pass two Thread objects (A,B) to a Thread, and set as B as a member of A, or vice versa
++this will raise an exception when you try to access the member in the context that created A and B
++if the Thread has been destroyed.
++
++This is because A and B inside the Thread are objects that represent the original objects but are not physically
++the same.
++
++Explicitly casting Threaded to object upon access ensures you are referencing the original object.
++
++This could be dangerous if misused ... please don't misuse it ...
++--FILE--
++<?php
++class Test extends Thread {
++	
++	public function __construct(Threaded $thing, Threaded $member) {
++		$this->thing = $thing;
++		$this->member = $member;
++	}
++
++	public function run() {
++		$this->thing->member = 
++			(object) $this->member;
++
++	}
++	
++	private $thing;
++	private $member;
++}
++
++$thing = new Threaded();
++$member = new Threaded();
++
++$test = new Test($thing, $member);
++
++$test->start() && $test->join();
++
++var_dump($thing);
++?>
++--EXPECT--
++object(Threaded)#1 (1) {
++  ["member"]=>
++  object(Threaded)#2 (0) {
++  }
++}
++
++
++
+diff --git a/ext/pthreads/tests/object-comparison.phpt b/ext/pthreads/tests/object-comparison.phpt
+new file mode 100644
+index 00000000..4104644f
+--- /dev/null
++++ b/ext/pthreads/tests/object-comparison.phpt
+@@ -0,0 +1,41 @@
++--TEST--
++Test object comparison
++--DESCRIPTION--
++Threaded object comparison was broken in pthreads v2, the handler was not implemented.
++
++In pthreads v3 we implement the handler, but not in the way Zend is expecting us too.
++
++To traverse (and reconstruct in most cases) the property store in order to perform a comparison is extremely inefficient.
++
++In addition, because of the way pthreads works, it might be useful to determine if two references from two different contexts are infact references
++to the same object from some other context.
++
++So pthreads v3 implements the comparison handler and returns true when two references are for the same Threaded object, this differs from normal object
++comparison which would return true if the property tables of two distinct objects are uniform.
++--FILE--
++<?php
++class Test extends Thread {
++	public function __construct(Threaded ... $args) {
++		$this->args = $args;
++	}
++
++	public function run () {
++		var_dump($this->args[0] == $this->args[1]);
++	}
++}
++
++$a = new Threaded();
++$b = new Threaded();
++
++$test = new Test($a, $b);	# bool(false)
++$test->start();
++$test->join();
++
++$test = new Test($a, $a);	# bool(true)
++$test->start();
++$test->join();
++?>
++--EXPECT--
++bool(false)
++bool(true)
++
+diff --git a/ext/pthreads/tests/oomethods.phpt b/ext/pthreads/tests/oomethods.phpt
+new file mode 100644
+index 00000000..8630f400
+--- /dev/null
++++ b/ext/pthreads/tests/oomethods.phpt
+@@ -0,0 +1,23 @@
++--TEST--
++Test access to user defined methods in the object context
++--DESCRIPTION--
++User methods are now imported from your declared class into the thread
++--FILE--
++<?php
++class ThreadTest extends Thread {
++	public function objectTest(){
++		return $this->value;
++	}
++	
++	public function run(){
++		$this->value = 1;
++	}
++}
++$thread = new ThreadTest();
++if($thread->start()) {
++	$thread->join();
++	var_dump($thread->objectTest());
++}
++?>
++--EXPECT--
++int(1)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/pools-default.phpt b/ext/pthreads/tests/pools-default.phpt
+new file mode 100644
+index 00000000..0182c00e
+--- /dev/null
++++ b/ext/pthreads/tests/pools-default.phpt
+@@ -0,0 +1,44 @@
++--TEST--
++Test pool defaults
++--DESCRIPTION--
++This test verifies pool defaults
++--FILE--
++<?php
++class Work extends Threaded {
++	public function run() {
++		var_dump($this);
++	}
++}
++
++$pool = new Pool(1);
++$pool->submit(new Work());
++$pool->shutdown();
++
++$pool->collect(function(Collectable $work) {
++	return $work->isGarbage();
++});
++
++var_dump($pool);
++?>
++--EXPECTF--
++object(Work)#%d (%d) {
++  ["worker"]=>
++  object(Worker)#%d (0) {
++  }
++}
++object(Pool)#%d (%d) {
++  ["size":protected]=>
++  int(1)
++  ["class":protected]=>
++  string(6) "Worker"
++  ["workers":protected]=>
++  array(0) {
++  }
++  ["ctor":protected]=>
++  NULL
++  ["last":protected]=>
++  int(1)
++}
++
++
++
+diff --git a/ext/pthreads/tests/pools.phpt b/ext/pthreads/tests/pools.phpt
+new file mode 100644
+index 00000000..829b8d2c
+--- /dev/null
++++ b/ext/pthreads/tests/pools.phpt
+@@ -0,0 +1,72 @@
++--TEST--
++Test pooling
++--DESCRIPTION--
++This test verifies the functionality of selective inheritance
++--FILE--
++<?php
++class WebWorker extends Worker {
++	public function __construct(SafeLog $logger) {
++		$this->logger = $logger;
++	}
++	
++	protected $logger;	
++}
++
++class WebWork extends Threaded {
++	public function __construct(int $id) {
++		$this->id = $id;
++	}
++
++	public function run() {
++		$this->worker
++			->logger
++			->log("%s(%d) executing in Thread #%lu",
++				  __CLASS__, $this->id, $this->worker->getThreadId());
++	}
++
++	protected $id;
++}
++
++class SafeLog extends Threaded {
++	public function log($message, ... $args) {
++		$this->synchronized(function($message, ... $args) {
++			echo vsprintf("{$message}\n", ...$args);
++		}, $message, $args);
++	}
++}
++
++$pool = new Pool(8, 'WebWorker', array(new SafeLog()));
++while (@$i++<10)
++	$pool->submit(new WebWork($i));
++$pool->shutdown();
++var_dump($pool);
++?>
++--EXPECTF--
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++WebWork(%d) executing in Thread #%d
++object(Pool)#%d (%d) {
++  ["size":protected]=>
++  int(8)
++  ["class":protected]=>
++  string(9) "WebWorker"
++  ["workers":protected]=>
++  array(0) {
++  }
++  ["ctor":protected]=>
++  array(1) {
++    [0]=>
++    object(SafeLog)#%d (0) {
++    }
++  }
++  ["last":protected]=>
++  int(%d)
++}
++
+diff --git a/ext/pthreads/tests/private-class-constant-fetching.phpt b/ext/pthreads/tests/private-class-constant-fetching.phpt
+new file mode 100644
+index 00000000..926c6b47
+--- /dev/null
++++ b/ext/pthreads/tests/private-class-constant-fetching.phpt
+@@ -0,0 +1,50 @@
++--TEST--
++Private class constant fetching
++--DESCRIPTION--
++This test ensures that private class constants are properly fetched when
++static:: is used.
++--FILE--
++<?php
++class A
++{
++	private const A = 1;
++	private const B = [2, []];
++	private const C = true;
++	private const D = false;
++	private const E = 1.1;
++	private const F = 'a';
++
++	public static function getA()
++	{
++		var_dump(
++			static::A,
++			static::B,
++			static::C,
++			static::D,
++			static::E,
++			static::F
++		);
++	}
++}
++
++$t = new class extends Thread {
++	public function run()
++	{
++		A::getA();
++	}
++};
++
++$t->start() && $t->join();
++--EXPECT--
++int(1)
++array(2) {
++  [0]=>
++  int(2)
++  [1]=>
++  array(0) {
++  }
++}
++bool(true)
++bool(false)
++float(1.1)
++string(1) "a"
+diff --git a/ext/pthreads/tests/pthreads-inherit-none-class-constant-inherited.phpt b/ext/pthreads/tests/pthreads-inherit-none-class-constant-inherited.phpt
+new file mode 100644
+index 00000000..6b177724
+--- /dev/null
++++ b/ext/pthreads/tests/pthreads-inherit-none-class-constant-inherited.phpt
+@@ -0,0 +1,29 @@
++--TEST--
++Inherited class constant in PTHREADS_INHERIT_NONE thread
++--DESCRIPTION--
++This test ensures that inherited class constants are available inside in PTHREADS_INHERIT_NONE threads without error.
++--FILE--
++<?php
++class baseTest extends \Thread {
++
++    const TEST_CONSTANT = 0x00;
++
++    public function test() {
++        $this->synchronized(function () {
++            var_dump("Constant: " . self::TEST_CONSTANT);
++        });
++    }
++}
++
++class testClass extends baseTest {
++
++    public function run() {
++        $this->test();
++    }
++}
++
++$x = new testClass();
++$x->start(PTHREADS_INHERIT_NONE);
++$x->join();
++--EXPECT--
++string(11) "Constant: 0"
+diff --git a/ext/pthreads/tests/pthreads-inherit-none-class-constant.phpt b/ext/pthreads/tests/pthreads-inherit-none-class-constant.phpt
+new file mode 100644
+index 00000000..45bbb381
+--- /dev/null
++++ b/ext/pthreads/tests/pthreads-inherit-none-class-constant.phpt
+@@ -0,0 +1,20 @@
++--TEST--
++Class constant in PTHREADS_INHERIT_NONE thread
++--DESCRIPTION--
++This test ensures that class constants are available inside in PTHREADS_INHERIT_NONE threads without error.
++--FILE--
++<?php
++class testClass extends \Thread {
++
++    const TEST_CONSTANT=0x00;
++
++    public function run() {
++        var_dump('works');
++    }
++}
++
++$x = new testClass();
++$x->start(PTHREADS_INHERIT_NONE);
++$x->join();
++--EXPECT--
++string(5) "works"
+diff --git a/ext/pthreads/tests/recv-overload.phpt b/ext/pthreads/tests/recv-overload.phpt
+new file mode 100644
+index 00000000..c256dc90
+--- /dev/null
++++ b/ext/pthreads/tests/recv-overload.phpt
+@@ -0,0 +1,46 @@
++--TEST--
++Test RECV overload
++--DESCRIPTION--
++When using the explicit cast of objects, type hinting can be broken.
++
++The class in the current context doesn't match the class of the object.
++
++pthreads overrides ZEND_RECV to fix the problem, nothing else is affected.
++--FILE--
++<?php
++class Test extends Thread {
++	
++	public function __construct(Custom $member) {
++		$this->member = $member;
++	}
++
++	public function run() {
++		$this->method(
++			(object)$this->member);
++	}
++
++	public function method(Custom $member) {}
++
++	private $member;
++}
++
++class Custom extends Threaded {}
++
++$member = new Custom();
++
++$test = new Test($member);
++
++$test->start() && $test->join();
++
++var_dump($test);
++?>
++--EXPECT--
++object(Test)#2 (1) {
++  ["member"]=>
++  object(Custom)#1 (0) {
++  }
++}
++
++
++
++
+diff --git a/ext/pthreads/tests/resource-keeping.phpt b/ext/pthreads/tests/resource-keeping.phpt
+new file mode 100644
+index 00000000..d4eeef4a
+--- /dev/null
++++ b/ext/pthreads/tests/resource-keeping.phpt
+@@ -0,0 +1,149 @@
++--TEST--
++Test sane handling of resources from foreign contexts
++--DESCRIPTION--
++Test that resources from other thread contexts can properly be accessed and are only destroyed by their owner
++--FILE--
++<?php
++class myWorker extends Worker {
++    private $foreignResource;
++
++    public function __construct($useResource) {
++        $this->foreignResource = $useResource;
++        var_dump([
++            'Resource received and stored:',
++            is_resource($useResource),
++            is_resource($this->foreignResource),
++        ]);
++    }
++
++    public function run () {
++
++        // polute EG(regular_list) with another resource of our own
++        $localThreadFP = tmpfile();
++
++        var_dump(array(
++            'Foreign resource conversion test:',
++
++            // test conversion followed by destruction
++            // the resource shall be added and removed from
++            // PTHREADS_ZG(resources) and EG(regular_list) by the following
++            // statement:
++
++            is_resource($this->foreignResource),
++
++            // now make the resource permanently available locally to tasks by
++            // storing it in a variable. It will stay in PTHREADS_ZG(resources)
++            // and EG(regular_list) until the worker is shutdown):
++
++            is_resource($foreignResource = $this->foreignResource),
++
++            // test that we still get the correct resource when it's already in
++            // EG(regular_list)
++            $foreignResource === $this->foreignResource
++        ));
++
++        fclose($localThreadFP);
++    }
++
++}
++
++class Work extends Threaded {
++
++    public function run () {
++	$foreignResource = $this->worker->foreignResource;
++
++        var_dump(array(
++            'Foreign resource is accessible from task:',
++            is_resource($foreignResource),
++            $foreignResource === $this->worker->foreignResource,
++            'wrote ' . fwrite($foreignResource, '42') . ' bytes',
++        ));
++
++    }
++}
++
++$fp = tmpfile();
++
++$task = new Work();
++
++$worker = new myWorker($fp);
++$worker->start();
++$worker->stack($task);
++
++var_dump(array(
++    'Our worker is shutdown:',
++    $worker->shutdown(),
++));
++
++var_dump(array(
++    'Our resource is still valid:',
++    is_resource($fp) && rewind($fp),
++    fread($fp, 2) === "42",
++));
++
++var_dump(array(
++    'Our resource can be closed:',
++     fclose($fp),
++ ));
++
++var_dump(array(
++    'Our resource is closed:',
++    !is_resource($fp),
++));
++?>
++--EXPECT--
++array(3) {
++  [0]=>
++  string(29) "Resource received and stored:"
++  [1]=>
++  bool(true)
++  [2]=>
++  bool(true)
++}
++array(4) {
++  [0]=>
++  string(33) "Foreign resource conversion test:"
++  [1]=>
++  bool(true)
++  [2]=>
++  bool(true)
++  [3]=>
++  bool(true)
++}
++array(4) {
++  [0]=>
++  string(41) "Foreign resource is accessible from task:"
++  [1]=>
++  bool(true)
++  [2]=>
++  bool(true)
++  [3]=>
++  string(13) "wrote 2 bytes"
++}
++array(2) {
++  [0]=>
++  string(23) "Our worker is shutdown:"
++  [1]=>
++  bool(true)
++}
++array(3) {
++  [0]=>
++  string(28) "Our resource is still valid:"
++  [1]=>
++  bool(true)
++  [2]=>
++  bool(true)
++}
++array(2) {
++  [0]=>
++  string(27) "Our resource can be closed:"
++  [1]=>
++  bool(true)
++}
++array(2) {
++  [0]=>
++  string(23) "Our resource is closed:"
++  [1]=>
++  bool(true)
++}
++
+diff --git a/ext/pthreads/tests/return-types.phpt b/ext/pthreads/tests/return-types.phpt
+new file mode 100644
+index 00000000..0945944f
+--- /dev/null
++++ b/ext/pthreads/tests/return-types.phpt
+@@ -0,0 +1,20 @@
++--TEST--
++Test return types
++--DESCRIPTION--
++This test verifies that functions with a typed return are copied and execute properly
++--FILE--
++<?php
++function some() : string {
++	return __FUNCTION__;
++}
++
++$thread = new class extends Thread {
++	public function run () {
++		var_dump(some());
++	}
++};
++
++$thread->start();
++$thread->join();
++--EXPECT--
++string(4) "some"
+diff --git a/ext/pthreads/tests/run-time-cache-corruption.phpt b/ext/pthreads/tests/run-time-cache-corruption.phpt
+new file mode 100644
+index 00000000..8777a554
+--- /dev/null
++++ b/ext/pthreads/tests/run-time-cache-corruption.phpt
+@@ -0,0 +1,58 @@
++--TEST--
++Test run time cache bug #494
++--DESCRIPTION--
++Runtime Caches were being incorrectly used (shared!), this caused strange behaviour for various things
++--FILE--
++<?php
++interface TestInterface {}
++
++class DebugClass implements TestInterface {}
++
++class Some extends Threaded {
++    public static function staticNess() {
++        $closure = function() : DebugClass {
++            return (new \DebugClass());
++        };
++
++        $objCreated = $closure();
++        var_dump((new \ReflectionClass($objCreated))->getInterfaceNames());
++		var_dump($objCreated instanceof \TestInterface);
++    }
++}
++
++class Test extends Thread {
++    public function run(){
++        Some::staticNess();
++    }
++}
++
++Some::staticNess();
++
++$test = new class extends Thread {
++	public function run() {
++		Some::staticNess();
++	}
++};
++
++$test->start(PTHREADS_INHERIT_NONE | 
++			 PTHREADS_INHERIT_INI | 
++			 PTHREADS_ALLOW_HEADERS | 
++			 PTHREADS_INHERIT_COMMENTS | 
++			 PTHREADS_INHERIT_INCLUDES | 
++			 PTHREADS_INHERIT_FUNCTIONS | 
++			 PTHREADS_INHERIT_CLASSES) && $test->join();
++?>
++--EXPECT--
++array(1) {
++  [0]=>
++  string(13) "TestInterface"
++}
++bool(true)
++array(1) {
++  [0]=>
++  string(13) "TestInterface"
++}
++bool(true)
++
++
++
+diff --git a/ext/pthreads/tests/runtime-inheritance-volatile.phpt b/ext/pthreads/tests/runtime-inheritance-volatile.phpt
+new file mode 100644
+index 00000000..faea81f6
+--- /dev/null
++++ b/ext/pthreads/tests/runtime-inheritance-volatile.phpt
+@@ -0,0 +1,23 @@
++--TEST--
++Test runtime extension of Volatile
++--DESCRIPTION--
++This test verifies that extend() applies the scope it was called in, not executed in
++--FILE--
++<?php
++class Other {}
++
++class Test extends Other {
++    public function one() {}
++}
++
++/* force zend to declare Other extends Volatile */
++Volatile::extend("Other");
++
++$test = new Test();
++
++var_dump($test instanceof Volatile,
++         $test instanceof Other);
++?>
++--EXPECT--
++bool(true)
++bool(true)
+diff --git a/ext/pthreads/tests/runtime-inheritance.phpt b/ext/pthreads/tests/runtime-inheritance.phpt
+new file mode 100644
+index 00000000..19b4bbfa
+--- /dev/null
++++ b/ext/pthreads/tests/runtime-inheritance.phpt
+@@ -0,0 +1,23 @@
++--TEST--
++Test runtime extension
++--DESCRIPTION--
++This test verifies functionality of runtime extension
++--FILE--
++<?php
++class Other {}
++
++class Test extends Other {
++    public function one() {}
++}
++
++/* force zend to declare Other extends Threaded */
++Threaded::extend("Other");
++
++$test = new Test();
++
++var_dump($test instanceof Threaded,
++         $test instanceof Other);
++?>
++--EXPECT--
++bool(true)
++bool(true)
+diff --git a/ext/pthreads/tests/selective-inheritance.phpt b/ext/pthreads/tests/selective-inheritance.phpt
+new file mode 100644
+index 00000000..7e4fce6e
+--- /dev/null
++++ b/ext/pthreads/tests/selective-inheritance.phpt
+@@ -0,0 +1,49 @@
++--TEST--
++Test selective inheritance
++--DESCRIPTION--
++This test verifies the functionality of selective inheritance
++--FILE--
++<?php
++function TestFunction(){
++	return __FUNCTION__;
++}
++
++define ("TEST_CONSTANT", true);
++
++class TestClass {}
++
++class TestThread extends Thread {
++	public function run() { 
++		var_dump(function_exists("TestFunction"));
++		var_dump(defined("TEST_CONSTANT"));
++		var_dump(class_exists("TestClass")); 
++	}
++}
++
++$thread = new TestThread();
++$thread->start();
++$thread->join();
++unset($thread);
++
++$thread = new TestThread();
++$thread->start(PTHREADS_INHERIT_NONE);
++$thread->join();
++unset($thread);
++
++$thread = new TestThread();
++$thread->start(PTHREADS_INHERIT_FUNCTIONS | PTHREADS_INHERIT_CLASSES);
++$thread->join();
++unset($thread);
++?>
++--EXPECT--
++bool(true)
++bool(true)
++bool(true)
++bool(false)
++bool(false)
++bool(false)
++bool(true)
++bool(false)
++bool(true)
++
++
+diff --git a/ext/pthreads/tests/shift-pop.phpt b/ext/pthreads/tests/shift-pop.phpt
+new file mode 100644
+index 00000000..e70e5dd0
+--- /dev/null
++++ b/ext/pthreads/tests/shift-pop.phpt
+@@ -0,0 +1,342 @@
++--TEST--
++Test shift and pop
++--DESCRIPTION--
++This test verifies that shift and pop functionality are working
++--FILE--
++<?php
++class S extends Threaded {
++	public function run(){}
++}
++$s = new S();
++$s[] = "help";
++var_dump($s);
++var_dump($s->shift());
++var_dump($s);
++$s[] = "next";
++var_dump($s);
++var_dump($s->pop());
++var_dump($s);
++while (@$i++<100)
++    $s[$i]=$i;
++var_dump($s);
++while (($next = $s->pop())) {
++    var_dump($next);
++}
++?>
++--EXPECT--
++object(S)#1 (1) {
++  [0]=>
++  string(4) "help"
++}
++string(4) "help"
++object(S)#1 (0) {
++}
++object(S)#1 (1) {
++  [1]=>
++  string(4) "next"
++}
++string(4) "next"
++object(S)#1 (0) {
++}
++object(S)#1 (100) {
++  [1]=>
++  int(1)
++  [2]=>
++  int(2)
++  [3]=>
++  int(3)
++  [4]=>
++  int(4)
++  [5]=>
++  int(5)
++  [6]=>
++  int(6)
++  [7]=>
++  int(7)
++  [8]=>
++  int(8)
++  [9]=>
++  int(9)
++  [10]=>
++  int(10)
++  [11]=>
++  int(11)
++  [12]=>
++  int(12)
++  [13]=>
++  int(13)
++  [14]=>
++  int(14)
++  [15]=>
++  int(15)
++  [16]=>
++  int(16)
++  [17]=>
++  int(17)
++  [18]=>
++  int(18)
++  [19]=>
++  int(19)
++  [20]=>
++  int(20)
++  [21]=>
++  int(21)
++  [22]=>
++  int(22)
++  [23]=>
++  int(23)
++  [24]=>
++  int(24)
++  [25]=>
++  int(25)
++  [26]=>
++  int(26)
++  [27]=>
++  int(27)
++  [28]=>
++  int(28)
++  [29]=>
++  int(29)
++  [30]=>
++  int(30)
++  [31]=>
++  int(31)
++  [32]=>
++  int(32)
++  [33]=>
++  int(33)
++  [34]=>
++  int(34)
++  [35]=>
++  int(35)
++  [36]=>
++  int(36)
++  [37]=>
++  int(37)
++  [38]=>
++  int(38)
++  [39]=>
++  int(39)
++  [40]=>
++  int(40)
++  [41]=>
++  int(41)
++  [42]=>
++  int(42)
++  [43]=>
++  int(43)
++  [44]=>
++  int(44)
++  [45]=>
++  int(45)
++  [46]=>
++  int(46)
++  [47]=>
++  int(47)
++  [48]=>
++  int(48)
++  [49]=>
++  int(49)
++  [50]=>
++  int(50)
++  [51]=>
++  int(51)
++  [52]=>
++  int(52)
++  [53]=>
++  int(53)
++  [54]=>
++  int(54)
++  [55]=>
++  int(55)
++  [56]=>
++  int(56)
++  [57]=>
++  int(57)
++  [58]=>
++  int(58)
++  [59]=>
++  int(59)
++  [60]=>
++  int(60)
++  [61]=>
++  int(61)
++  [62]=>
++  int(62)
++  [63]=>
++  int(63)
++  [64]=>
++  int(64)
++  [65]=>
++  int(65)
++  [66]=>
++  int(66)
++  [67]=>
++  int(67)
++  [68]=>
++  int(68)
++  [69]=>
++  int(69)
++  [70]=>
++  int(70)
++  [71]=>
++  int(71)
++  [72]=>
++  int(72)
++  [73]=>
++  int(73)
++  [74]=>
++  int(74)
++  [75]=>
++  int(75)
++  [76]=>
++  int(76)
++  [77]=>
++  int(77)
++  [78]=>
++  int(78)
++  [79]=>
++  int(79)
++  [80]=>
++  int(80)
++  [81]=>
++  int(81)
++  [82]=>
++  int(82)
++  [83]=>
++  int(83)
++  [84]=>
++  int(84)
++  [85]=>
++  int(85)
++  [86]=>
++  int(86)
++  [87]=>
++  int(87)
++  [88]=>
++  int(88)
++  [89]=>
++  int(89)
++  [90]=>
++  int(90)
++  [91]=>
++  int(91)
++  [92]=>
++  int(92)
++  [93]=>
++  int(93)
++  [94]=>
++  int(94)
++  [95]=>
++  int(95)
++  [96]=>
++  int(96)
++  [97]=>
++  int(97)
++  [98]=>
++  int(98)
++  [99]=>
++  int(99)
++  [100]=>
++  int(100)
++}
++int(100)
++int(99)
++int(98)
++int(97)
++int(96)
++int(95)
++int(94)
++int(93)
++int(92)
++int(91)
++int(90)
++int(89)
++int(88)
++int(87)
++int(86)
++int(85)
++int(84)
++int(83)
++int(82)
++int(81)
++int(80)
++int(79)
++int(78)
++int(77)
++int(76)
++int(75)
++int(74)
++int(73)
++int(72)
++int(71)
++int(70)
++int(69)
++int(68)
++int(67)
++int(66)
++int(65)
++int(64)
++int(63)
++int(62)
++int(61)
++int(60)
++int(59)
++int(58)
++int(57)
++int(56)
++int(55)
++int(54)
++int(53)
++int(52)
++int(51)
++int(50)
++int(49)
++int(48)
++int(47)
++int(46)
++int(45)
++int(44)
++int(43)
++int(42)
++int(41)
++int(40)
++int(39)
++int(38)
++int(37)
++int(36)
++int(35)
++int(34)
++int(33)
++int(32)
++int(31)
++int(30)
++int(29)
++int(28)
++int(27)
++int(26)
++int(25)
++int(24)
++int(23)
++int(22)
++int(21)
++int(20)
++int(19)
++int(18)
++int(17)
++int(16)
++int(15)
++int(14)
++int(13)
++int(12)
++int(11)
++int(10)
++int(9)
++int(8)
++int(7)
++int(6)
++int(5)
++int(4)
++int(3)
++int(2)
++int(1)
+diff --git a/ext/pthreads/tests/shutdown-handler.phpt b/ext/pthreads/tests/shutdown-handler.phpt
+new file mode 100644
+index 00000000..fbe89ca9
+--- /dev/null
++++ b/ext/pthreads/tests/shutdown-handler.phpt
+@@ -0,0 +1,21 @@
++--TEST--
++Test shutdown handlers #204
++--DESCRIPTION--
++Shutdown handlers that were closures were causing segfaults
++--FILE--
++<?php
++class Test extends Thread {
++        public function run() {
++                register_shutdown_function(function(){
++                        var_dump(new stdClass());
++                });
++        }
++}
++
++$test = new Test();
++$test->start();
++?>
++--EXPECTF--
++object(stdClass)#%d (0) {
++}
++
+diff --git a/ext/pthreads/tests/socket-bind.phpt b/ext/pthreads/tests/socket-bind.phpt
+new file mode 100644
+index 00000000..8f16c35b
+--- /dev/null
++++ b/ext/pthreads/tests/socket-bind.phpt
+@@ -0,0 +1,38 @@
++--TEST--
++socket_bind - basic test
++--CREDITS--
++Copied from php/php-src and adjusted, originally created by 
++Florian Anderiasch
++fa@php.net
++--SKIPIF--
++<?php
++    if (getenv("SKIP_ONLINE_TESTS")) {
++        die("skip test requiring internet connection");
++    }
++?>
++--FILE--
++<?php
++	$rand = rand(1,999);
++    $socket = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, \Socket::SOL_TCP);
++    $bind   = $socket->bind('0.0.0.0', 31330+$rand);
++    var_dump($bind);
++
++    // Connect to destination address
++    $s_conn  = $socket->connect('www.php.net', 80);
++    var_dump($s_conn);
++
++    // Write
++    $request = 'GET / HTTP/1.1' . "\r\n";
++    $s_write = $socket->write($request);
++    var_dump($s_write);
++
++    // Close
++    $s_close = $socket->close();
++    var_dump($s_close);
++?>
++
++--EXPECTF--
++bool(true)
++bool(true)
++int(16)
++NULL
+diff --git a/ext/pthreads/tests/socket-connect-error-unix.phpt b/ext/pthreads/tests/socket-connect-error-unix.phpt
+new file mode 100644
+index 00000000..774d9ea8
+--- /dev/null
++++ b/ext/pthreads/tests/socket-connect-error-unix.phpt
+@@ -0,0 +1,43 @@
++--TEST--
++Test error cases when creating a socket on non-win32
++--CREDITS--
++Copied from php/php-src and adjusted, originally created by 
++Russell Flynn <russ@redpill-linpro.com>
++#PHPTestFest2009 Norway 2009-06-09 \o/
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) == 'WIN') {
++	die('skip.. Not valid for Windows');
++}
++--INI--
++error_reporting=E_ALL
++display_errors=1
++--FILE--
++<?php
++  // Test with no arguments
++  $server = new Socket();
++  
++  // Test with less arguments than required
++  $server = new Socket(\Socket::SOCK_STREAM, getprotobyname('tcp'));
++  
++  try {
++    // Test with non integer parameters
++    $server = new Socket(array(), 1, 1);
++  } catch(Throwable $throwable) {
++    var_dump($throwable->getMessage());
++  }
++
++  try {
++    // Test with unknown domain
++    $server = new Socket(\Socket::AF_INET + 1000, \Socket::SOCK_STREAM, 0);
++  } catch(Throwable $throwable) {
++    var_dump($throwable->getMessage());
++  }
++  
++?>
++--EXPECTF--
++Warning: Socket::__construct() expects exactly 3 parameters, 0 given in %s on line %d
++
++Warning: Socket::__construct() expects exactly 3 parameters, 2 given in %s on line %d
++string(%d) "Argument 1 passed to Socket::__construct() must be of the type %s, array given"
++string(%d) "Unable to create socket (%d): Address family not supported by protocol"
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-connect-error-win32.phpt b/ext/pthreads/tests/socket-connect-error-win32.phpt
+new file mode 100644
+index 00000000..1fb6e901
+--- /dev/null
++++ b/ext/pthreads/tests/socket-connect-error-win32.phpt
+@@ -0,0 +1,44 @@
++--TEST--
++Test error cases when creating a socket on win32 only
++--CREDITS--
++Copied from php/php-src and adjusted, originally created by 
++Russell Flynn <russ@redpill-linpro.com>
++#PHPTestFest2009 Norway 2009-06-09 \o/
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) != 'WIN') {
++	die('skip.. only valid for Windows');
++}
++--INI--
++error_reporting=E_ALL
++display_errors=1
++--FILE--
++<?php
++  // Test with no arguments
++  $server = new Socket();
++  
++  // Test with less arguments than required
++  $server = new Socket(\Socket::SOCK_STREAM, getprotobyname('tcp'));
++  
++  try {
++    // Test with non integer parameters
++    $server = new Socket(array(), 1, 1);
++  } catch(Throwable $throwable) {
++    var_dump($throwable->getMessage());
++  }
++
++  try {
++    // Test with unknown domain
++    $server = new Socket(\Socket::AF_INET + 1000, \Socket::SOCK_STREAM, 0);
++  } catch(Throwable $throwable) {
++    var_dump($throwable->getMessage());
++  }
++  
++?>
++--EXPECTF--
++Warning: Socket::__construct() expects exactly 3 parameters, 0 given in %s on line %d
++
++Warning: Socket::__construct() expects exactly 3 parameters, 2 given in %s on line %d
++string(%d) "Argument 1 passed to Socket::__construct() must be of the type %s, array given"
++string(%d) "Unable to create socket (%d): An address incompatible with the requested protocol was used.
++"
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-connect-params-af-inet.phpt b/ext/pthreads/tests/socket-connect-params-af-inet.phpt
+new file mode 100644
+index 00000000..333d9275
+--- /dev/null
++++ b/ext/pthreads/tests/socket-connect-params-af-inet.phpt
+@@ -0,0 +1,21 @@
++--TEST--
++Socket::connect() - AF_INET - test with empty parameters
++--FILE--
++<?php
++    $rand = rand(1,999);
++    $socket = new \Socket(\Socket::AF_INET, \Socket::SOCK_DGRAM, 0);
++    $socket->bind('0.0.0.0');
++    // wrong parameter count
++    $socket->connect();
++    try {
++        $socket->connect('0.0.0.0');
++    } catch(Exception $exception) {
++        var_dump($exception->getMessage());
++    }
++    $socket->connect('127.0.0.1', 31330+$rand);
++    $socket->close();
++?>
++--EXPECTF--
++
++Warning: Socket::connect() expects at least 1 parameter, 0 given in %s on line %i
++string(43) "Socket of type AF_INET requires 2 arguments"
+diff --git a/ext/pthreads/tests/socket-connect-params-af-unix.phpt b/ext/pthreads/tests/socket-connect-params-af-unix.phpt
+new file mode 100644
+index 00000000..2cfbd7af
+--- /dev/null
++++ b/ext/pthreads/tests/socket-connect-params-af-unix.phpt
+@@ -0,0 +1,32 @@
++--TEST--
++Socket::connect() - AF_UNIX - test with empty parameters
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) == 'WIN') {
++	die('skip.. Not valid for Windows');
++}
++--FILE--
++<?php
++    $rand = rand(1,999);
++    $socket = new \Socket(\Socket::AF_UNIX,\Socket::SOCK_DGRAM, 0);
++
++    if (!$socket->setBlocking(false)) {
++        die('Unable to set nonblocking mode for socket');
++    }
++    $address = sprintf("/tmp/%s.sock", uniqid());
++
++    if (file_exists($address))
++        die('Temporary file socket already exists.');
++
++    if (!$socket->bind($address)) {
++        die("Unable to bind to $address");
++    }
++    $socket->connect();
++    $socket->connect($address);
++    $socket->connect($address, 31330+$rand);
++
++    $socket->close();
++?>
++--EXPECTF--
++
++Warning: Socket::connect() expects at least 1 parameter, 0 given in %s on line %i
+diff --git a/ext/pthreads/tests/socket-construct.phpt b/ext/pthreads/tests/socket-construct.phpt
+new file mode 100644
+index 00000000..bf5ec025
+--- /dev/null
++++ b/ext/pthreads/tests/socket-construct.phpt
+@@ -0,0 +1,14 @@
++--TEST--
++Test creating and closing sockets
++--DESCRIPTION--
++Test that creating and closing sockets works as expected on all platforms (gh issue #798)
++--FILE--
++<?php
++$socket = new \Socket(\Socket::AF_INET, \Socket::SOCK_DGRAM, \Socket::SOL_UDP);
++echo "created\n";
++$socket->close();
++echo "closed\n";
++?>
++--EXPECTF--
++created
++closed
+diff --git a/ext/pthreads/tests/socket-getoption.phpt b/ext/pthreads/tests/socket-getoption.phpt
+new file mode 100644
+index 00000000..1ca88958
+--- /dev/null
++++ b/ext/pthreads/tests/socket-getoption.phpt
+@@ -0,0 +1,18 @@
++--TEST--
++Test of Socket::getOption() with and without parameters
++--FILE--
++<?php
++    $socket = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, \Socket::SOL_TCP);
++    $socket->getOption();
++
++    try {
++        $socket->getOption("hello", "world");
++    } catch(Throwable $throwable) {
++        var_dump($throwable->getMessage());
++    }
++    var_dump($socket->getOption(\Socket::SOL_SOCKET, \Socket::SO_REUSEADDR));
++?>
++--EXPECTF--
++Warning: Socket::getOption() expects exactly 2 parameters, 0 given in %s on line %i
++string(%i) "Argument 1 passed to Socket::getOption() must be of the type %s, string given"
++int(%i)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-getpeername-af-inet.phpt b/ext/pthreads/tests/socket-getpeername-af-inet.phpt
+new file mode 100644
+index 00000000..6e0085d5
+--- /dev/null
++++ b/ext/pthreads/tests/socket-getpeername-af-inet.phpt
+@@ -0,0 +1,32 @@
++--TEST--
++Basic test of Socket::getPeerName() with AF_INET sockets
++--FILE--
++<?php
++    $address = '127.0.0.1';
++    $port = 31330 + rand(1,999);
++
++    $socket = new \Socket(\Socket::AF_INET,\Socket::SOCK_STREAM, \Socket::SOL_TCP);
++
++    if (!$socket->bind($address, $port)) {
++        die("Unable to bind to $address");
++    }
++    $socket->listen(1);
++
++    $client = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, \Socket::SOL_TCP);
++    $client->connect($address, $port);
++
++    var_dump($client->getPeerName(), $client->getPeerName(false));
++
++    $client->close();
++?>
++--EXPECTF--
++array(2) {
++  ["host"]=>
++  string(9) "127.0.0.1"
++  ["port"]=>
++  int(%d)
++}
++array(1) {
++  ["host"]=>
++  string(9) "127.0.0.1"
++}
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-getpeername-af-unix.phpt b/ext/pthreads/tests/socket-getpeername-af-unix.phpt
+new file mode 100644
+index 00000000..e87730bc
+--- /dev/null
++++ b/ext/pthreads/tests/socket-getpeername-af-unix.phpt
+@@ -0,0 +1,30 @@
++--TEST--
++Basic test of Socket::getPeerName() with AF_UNIX sockets
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) == 'WIN') {
++	die('skip.. Not valid for Windows');
++}
++--FILE--
++<?php
++    $address = sprintf("/tmp/%s.sock", uniqid());
++
++    $socket = new \Socket(\Socket::AF_UNIX,\Socket::SOCK_STREAM, 0);
++
++    if (!$socket->bind($address)) {
++        die("Unable to bind to $address");
++    }
++    $socket->listen(1);
++
++    $client = new Socket(\Socket::AF_UNIX, \Socket::SOCK_STREAM, 0);
++    $client->connect($address);
++
++    var_dump($client->getPeerName());
++
++    $client->close();
++?>
++--EXPECTF--
++array(1) {
++  ["host"]=>
++  string(23) "/tmp/%s.sock"
++}
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-getsockname-af-inet.phpt b/ext/pthreads/tests/socket-getsockname-af-inet.phpt
+new file mode 100644
+index 00000000..ef31f27b
+--- /dev/null
++++ b/ext/pthreads/tests/socket-getsockname-af-inet.phpt
+@@ -0,0 +1,27 @@
++--TEST--
++Basic test of Socket::getSockName() with AF_INET sockets
++--FILE--
++<?php
++    $address = '127.0.0.1';
++    $port = 31330 + rand(1,999);
++
++    $inetSocket = new \Socket(\Socket::AF_INET,\Socket::SOCK_STREAM, \Socket::SOL_TCP);
++
++    if (!$inetSocket->bind($address, $port)) {
++        die("Unable to bind to $address");
++    }
++    var_dump($inetSocket->getSockName(), $inetSocket->getSockName(false));
++
++    $inetSocket->close();
++?>
++--EXPECTF--
++array(2) {
++  ["host"]=>
++  string(9) "127.0.0.1"
++  ["port"]=>
++  int(%d)
++}
++array(1) {
++  ["host"]=>
++  string(9) "127.0.0.1"
++}
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-getsockname-af-unix.phpt b/ext/pthreads/tests/socket-getsockname-af-unix.phpt
+new file mode 100644
+index 00000000..7c805ee1
+--- /dev/null
++++ b/ext/pthreads/tests/socket-getsockname-af-unix.phpt
+@@ -0,0 +1,28 @@
++--TEST--
++Basic test of Socket::getSockName() with AF_UNIX sockets
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) == 'WIN') {
++	die('skip.. Not valid for Windows');
++}
++--FILE--
++<?php
++    $address = sprintf("/tmp/%s.sock", uniqid());
++
++    if (file_exists($address))
++        die('Temporary file socket already exists.');
++
++    $unixSocket = new \Socket(\Socket::AF_UNIX,\Socket::SOCK_STREAM, 0);
++
++    if (!$unixSocket->bind($address)) {
++        die("Unable to bind to $address");
++    }
++    var_dump($unixSocket->getSockName());
++
++    $unixSocket->close();
++?>
++--EXPECTF--
++array(1) {
++  ["host"]=>
++  string(23) "/tmp/%s.sock"
++}
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-ipv4loop.phpt b/ext/pthreads/tests/socket-ipv4loop.phpt
+new file mode 100644
+index 00000000..c019d1c4
+--- /dev/null
++++ b/ext/pthreads/tests/socket-ipv4loop.phpt
+@@ -0,0 +1,55 @@
++--TEST--
++IPv4 Loopback test
++--FILE--
++<?php
++	/* Setup socket server */
++    $server = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, getprotobyname('tcp'));
++    if (!$server) {
++        die('Unable to create AF_INET socket [server]');
++    }
++    $bound = false;
++
++    for($port = 31337; $port < 31357; ++$port) {
++        try {
++            if ($server->bind('127.0.0.1', $port)) {
++                $bound = true;
++                break;
++            }
++        } catch (RuntimeException $re) {}
++    }
++	    
++    if (!$bound) {
++        die("Unable to bind to 127.0.0.1");
++    }
++    if (!$server->listen(2)) {
++        die('Unable to listen on socket');
++    }
++
++    /* Connect to it */
++    $client = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, getprotobyname('tcp'));
++    if (!$client) {
++        die('Unable to create AF_INET socket [client]');
++    }
++    if (!$client->connect('127.0.0.1', $port)) {
++        die('Unable to connect to server socket');
++    }
++
++    /* Accept that connection */
++    /** @var Socket $socket */
++    $socket = $server->accept(\Socket::class);
++    if (!$socket) {
++        die('Unable to accept connection');
++    }
++
++    $client->write("ABCdef123\n");
++
++    $data = $socket->read(10, 0, \Socket::BINARY_READ);
++    var_dump($data);
++
++    $client->close();
++    $socket->close();
++    $server->close();
++?>
++--EXPECT--
++string(10) "ABCdef123
++"
+diff --git a/ext/pthreads/tests/socket-ipv6loop.phpt b/ext/pthreads/tests/socket-ipv6loop.phpt
+new file mode 100644
+index 00000000..495551d3
+--- /dev/null
++++ b/ext/pthreads/tests/socket-ipv6loop.phpt
+@@ -0,0 +1,59 @@
++--TEST--
++IPv6 Loopback test
++--SKIPIF--
++<?php
++	require 'ipv6_skipif.inc';
++?>
++--FILE--
++<?php
++	/* Setup socket server */
++    $server = new Socket(\Socket::AF_INET6, \Socket::SOCK_STREAM, getprotobyname('tcp'));
++    if (!$server) {
++        die('Unable to create AF_INET6 socket [server]');
++    }
++    $bound = false;
++
++    for($port = 31337; $port < 31357; ++$port) {
++        try {
++            if ($server->bind('::1', $port)) {
++                $bound = true;
++                break;
++            }
++        } catch (RuntimeException $re) {}
++    }
++    
++    if (!$bound) {
++        die("Unable to bind to ['::1']:$port");
++    }
++    if (!$server->listen(2)) {
++        die('Unable to listen on socket');
++    }
++
++    /* Connect to it */
++    $client = new Socket(\Socket::AF_INET6, \Socket::SOCK_STREAM, getprotobyname('tcp'));
++    if (!$client) {
++        die('Unable to create AF_INET6 socket [client]');
++    }
++    if (!$client->connect('::1', $port)) {
++        die('Unable to connect to server socket');
++    }
++
++    /* Accept that connection */
++    /** @var Socket $socket */
++    $socket = $server->accept(\Socket::class);
++    if (!$socket) {
++        die('Unable to accept connection');
++    }
++
++    $client->write("ABCdef123\n");
++
++    $data = $socket->read(10, 0, \Socket::BINARY_READ);
++    var_dump($data);
++
++    $client->close();
++    $socket->close();
++    $server->close();
++?>
++--EXPECT--
++string(10) "ABCdef123
++"
+diff --git a/ext/pthreads/tests/socket-read-type.phpt b/ext/pthreads/tests/socket-read-type.phpt
+new file mode 100644
+index 00000000..93360e22
+--- /dev/null
++++ b/ext/pthreads/tests/socket-read-type.phpt
+@@ -0,0 +1,67 @@
++--TEST--
++Test of Socket::read() - testing type \Socket::NORMAL_READ and \Socket::BINARY_READ
++--FILE--
++<?php
++	/* Setup socket server */
++    $server = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, getprotobyname('tcp'));
++    if (!$server) {
++        die('Unable to create AF_INET socket [server]');
++    }
++    $bound = false;
++
++    for($port = 31337; $port < 31357; ++$port) {
++        try {
++            if ($server->bind('127.0.0.1', $port)) {
++                $bound = true;
++                break;
++            }
++        } catch (RuntimeException $re) {}
++    }
++    
++    if (!$bound) {
++        die("Unable to bind to 127.0.0.1");
++    }
++    if (!$server->listen(2)) {
++        die('Unable to listen on socket');
++    }
++
++    /* Connect to it */
++    $client = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, getprotobyname('tcp'));
++    if (!$client) {
++        die('Unable to create AF_INET socket [client]');
++    }
++    if (!$client->connect('127.0.0.1', $port)) {
++        die('Unable to connect to server socket');
++    }
++
++    /* Accept that connection */
++    /** @var Socket $socket */
++    $socket = $server->accept(\Socket::class);
++    if (!$socket) {
++        die('Unable to accept connection');
++    }
++
++    $client->write("ABCdef123\n456789");
++
++    $data = $socket->read(20, 0, \Socket::BINARY_READ);
++    var_dump($data);
++
++    $client->write("ABCdef123\n456789\n");
++
++    $data = $socket->read(20, 0, \Socket::NORMAL_READ);
++    var_dump($data);
++
++    $data = $socket->read(20, 0, \Socket::NORMAL_READ);
++    var_dump($data);
++
++    $client->close();
++    $socket->close();
++    $server->close();
++?>
++--EXPECT--
++string(16) "ABCdef123
++456789"
++string(10) "ABCdef123
++"
++string(7) "456789
++"
+diff --git a/ext/pthreads/tests/socket-select-unix.phpt b/ext/pthreads/tests/socket-select-unix.phpt
+new file mode 100644
+index 00000000..bcc2f435
+--- /dev/null
++++ b/ext/pthreads/tests/socket-select-unix.phpt
+@@ -0,0 +1,45 @@
++--TEST--
++Test parameter handling in Socket::select() on non-win32.
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) == 'WIN') {
++	die('skip.. Not valid for Windows');
++}
++--FILE--
++<?php
++    $socket = new \Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, \Socket::SOL_TCP);
++    $socket->listen();
++
++    try {
++        \Socket::select();
++    } catch(Throwable $throwable) {
++        var_dump($throwable->getMessage());
++    }
++    $read   = [$socket];
++    $write  = null;
++    $except = null;
++
++    // Valid arguments, return immediately
++    var_dump(\Socket::select($read, $write, $except, 0));
++
++    $read = [$socket];
++
++    // Valid sec argument, wait 1 second
++    var_dump(\Socket::select($read, $write, $except, 1, 0, $errno));
++    var_dump($errno);
++
++    $read = [$socket];
++
++    // Invalid sec argument, return immediately
++    var_dump(\Socket::select($read, $write, $except, -1, 0, $errno));
++    var_dump($errno);
++
++    $socket->close();
++--EXPECTF--
++
++Warning: Socket::select() expects at least 4 parameters, 0 given in %s on line %d
++int(0)
++int(0)
++int(0)
++bool(false)
++int(22)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-select-win32.phpt b/ext/pthreads/tests/socket-select-win32.phpt
+new file mode 100644
+index 00000000..e0142df9
+--- /dev/null
++++ b/ext/pthreads/tests/socket-select-win32.phpt
+@@ -0,0 +1,45 @@
++--TEST--
++Test parameter handling in Socket::select() on win32 only.
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) != 'WIN') {
++	die('skip.. only valid for Windows');
++}
++--FILE--
++<?php
++    $socket = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, \Socket::SOL_TCP);
++    $socket->listen();
++
++    try {
++        \Socket::select();
++    } catch(Throwable $throwable) {
++        var_dump($throwable->getMessage());
++    }
++    $read   = [$socket];
++    $write  = null;
++    $except = null;
++
++    // Valid arguments, return immediately
++    var_dump(\Socket::select($read, $write, $except, 0));
++
++    $read = [$socket];
++
++    // Valid sec argument, wait 1 second
++    var_dump(\Socket::select($read, $write, $except, 1, 0, $errno));
++    var_dump($errno);
++
++    $read = [$socket];
++
++    // Invalid sec argument, return immediately
++    var_dump(\Socket::select($read, $write, $except, -1, 0, $errno));
++    var_dump($errno);
++
++    $socket->close();
++--EXPECTF--
++
++Warning: Socket::select() expects at least 4 parameters, 0 given in %s on line %d
++int(0)
++int(0)
++int(0)
++int(0)
++int(0)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-sentto-recvfrom-ipv4-udp.phpt b/ext/pthreads/tests/socket-sentto-recvfrom-ipv4-udp.phpt
+new file mode 100644
+index 00000000..cb08a1ab
+--- /dev/null
++++ b/ext/pthreads/tests/socket-sentto-recvfrom-ipv4-udp.phpt
+@@ -0,0 +1,54 @@
++--TEST--
++Test if Socket::recvfrom() receives data sent by Socket::sendto() via IPv4 UDP
++--CREDITS--
++Copied from php/php-src and adjusted, originally created by 
++Falko Menge <mail at falko-menge dot de>
++PHP Testfest Berlin 2009-05-09
++--FILE--
++<?php
++    $socket = new Socket(\Socket::AF_INET, \Socket::SOCK_DGRAM, \Socket::SOL_UDP);
++    if (!$socket) {
++        die('Unable to create AF_INET socket');
++    }
++    if (!$socket->setBlocking(false)) {
++        die('Unable to set nonblocking mode for socket');
++    }
++
++    $address = '127.0.0.1';
++    $socket->sendto('', 1, 0, $address); // cause warning
++    if (!$socket->bind($address, 1223)) {
++        die("Unable to bind to $address:1223");
++    }
++    var_dump($socket->recvfrom($buf, 12, 0, $from, $port)); // false
++    
++    $msg = "Ping!";
++    $len = strlen($msg);
++    $bytes_sent = $socket->sendto($msg, $len, 0, $address, 1223);
++    if ($bytes_sent == -1) {
++        die('An error occurred while sending to the socket');
++    } else if ($bytes_sent != $len) {
++        die($bytes_sent . ' bytes have been sent instead of the ' . $len . ' bytes expected');
++    }
++
++    $from = "";
++    $port = 0;
++    $socket->recvfrom($buf, 12, 0); // cause warning
++    $socket->recvfrom($buf, 12, 0, $from); // cause warning
++    $bytes_received = $socket->recvfrom($buf, 12, 0, $from, $port);
++    if ($bytes_received == -1) {
++        die('An error occurred while receiving from the socket');
++    } else if ($bytes_received != $len) {
++        die($bytes_received . ' bytes have been received instead of the ' . $len . ' bytes expected');
++    }
++    echo "Received $buf from remote address $from and remote port $port" . PHP_EOL;
++
++    $socket->close();
++--EXPECTF--
++
++Warning: Wrong parameter count for Socket::sendto() in %s on line %d
++bool(false)
++
++Warning: Socket::recvfrom() expects at least 4 parameters, 3 given in %s on line %d
++
++Warning: Wrong parameter count for Socket::recvfrom() in %s on line %d
++Received Ping! from remote address 127.0.0.1 and remote port 1223
+diff --git a/ext/pthreads/tests/socket-sentto-recvfrom-ipv6-udp.phpt b/ext/pthreads/tests/socket-sentto-recvfrom-ipv6-udp.phpt
+new file mode 100644
+index 00000000..a2c48f55
+--- /dev/null
++++ b/ext/pthreads/tests/socket-sentto-recvfrom-ipv6-udp.phpt
+@@ -0,0 +1,56 @@
++--TEST--
++Test if Socket::recvfrom() receives data sent by Socket::sendto() via IPv6 UDP
++--CREDITS--
++Copied from php/php-src and adjusted, originally created by 
++Falko Menge <mail at falko-menge dot de>
++PHP Testfest Berlin 2009-05-09
++--SKIPIF--
++<?php
++require 'ipv6_skipif.inc';
++--FILE--
++<?php
++    $socket = new Socket(\Socket::AF_INET6, \Socket::SOCK_DGRAM, \Socket::SOL_UDP);
++    if (!$socket) {
++        die('Unable to create AF_INET6 socket');
++    }
++    if (!$socket->setBlocking(false)) {
++        die('Unable to set nonblocking mode for socket');
++    }
++    var_dump($socket->recvfrom($buf, 12, 0, $from, $port));
++    $address = '::1';
++    $socket->sendto('', 1, 0, $address); // cause warning
++    if (!$socket->bind($address, 1223)) {
++        die("Unable to bind to $address:1223");
++    }
++
++    $msg = "Ping!";
++    $len = strlen($msg);
++    $bytes_sent = $socket->sendto($msg, $len, 0, $address, 1223);
++    if ($bytes_sent == -1) {
++        die('An error occurred while sending to the socket');
++    } else if ($bytes_sent != $len) {
++        die($bytes_sent . ' bytes have been sent instead of the ' . $len . ' bytes expected');
++    }
++
++    $from = "";
++    $port = 0;
++    $socket->recvfrom($buf, 12, 0); // cause warning
++    $socket->recvfrom($buf, 12, 0, $from); // cause warning
++    $bytes_received = $socket->recvfrom($buf, 12, 0, $from, $port);
++    if ($bytes_received == -1) {
++        die('An error occurred while receiving from the socket');
++    } else if ($bytes_received != $len) {
++        die($bytes_received . ' bytes have been received instead of the ' . $len . ' bytes expected');
++    }
++    echo "Received $buf from remote address $from and remote port $port" . PHP_EOL;
++
++    $socket->close();
++--EXPECTF--
++bool(false)
++
++Warning: Wrong parameter count for Socket::sendto() in %s on line %d
++
++Warning: Socket::recvfrom() expects at least 4 parameters, 3 given in %s on line %d
++
++Warning: Wrong parameter count for Socket::recvfrom() in %s on line %d
++Received Ping! from remote address ::1 and remote port 1223
+diff --git a/ext/pthreads/tests/socket-sentto-recvfrom-unix.phpt b/ext/pthreads/tests/socket-sentto-recvfrom-unix.phpt
+new file mode 100644
+index 00000000..66ab441f
+--- /dev/null
++++ b/ext/pthreads/tests/socket-sentto-recvfrom-unix.phpt
+@@ -0,0 +1,65 @@
++--TEST--
++Test if Socket::recvfrom() receives data sent by Socket::sendto() through a Unix domain socket
++--CREDITS--
++Copied from php/php-src and adjusted, originally created by 
++Falko Menge <mail at falko-menge dot de>
++PHP Testfest Berlin 2009-05-09
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) == 'WIN') {
++	die('skip.. Not valid for Windows');
++}
++--FILE--
++<?php
++    try {
++        $socket = new \Socket(\Socket::AF_UNIX, \Socket::SOCK_DGRAM, \Socket::SOL_UDP);
++    }catch(RuntimeException $exception) {
++        var_dump($exception->getMessage());
++    }
++    $socket = new \Socket(\Socket::AF_UNIX, \Socket::SOCK_DGRAM, 0);
++    if (!$socket) {
++        die('Unable to create AF_UNIX socket');
++    }
++    if (!$socket->setBlocking(false)) {
++        die('Unable to set nonblocking mode for socket');
++    }
++    var_dump($socket->recvfrom($buf, 12, 0, $from, $port)); //false (EAGAIN, no warning)
++    $address = sprintf("/tmp/%s.sock", uniqid());
++    if (!$socket->bind($address)) {
++        die("Unable to bind to $address");
++    }
++
++    $msg = "Ping!";
++    $len = strlen($msg);
++    $bytes_sent = $socket->sendto($msg, $len, 0); // cause warning
++    $bytes_sent = $socket->sendto($msg, $len, 0, $address);
++    if ($bytes_sent == -1) {
++        @unlink($address);
++        die('An error occurred while sending to the socket');
++    } else if ($bytes_sent != $len) {
++        @unlink($address);
++        die($bytes_sent . ' bytes have been sent instead of the ' . $len . ' bytes expected');
++    }
++
++    $from = "";
++    var_dump($socket->recvfrom($buf, 0, 0, $from)); // expect false
++    $bytes_received = $socket->recvfrom($buf, 12, 0, $from);
++    if ($bytes_received == -1) {
++        @unlink($address);
++        die('An error occurred while receiving from the socket');
++    } else if ($bytes_received != $len) {
++        @unlink($address);
++        die($bytes_received . ' bytes have been received instead of the ' . $len . ' bytes expected');
++    }
++    echo "Received $buf";
++
++    $socket->close();
++    @unlink($address);
++?>
++--EXPECTF--
++string(%d) "Unable to create socket (%d): Protocol not supported"
++bool(false)
++
++Warning: Socket::sendto() expects at least 4 parameters, 3 given in %s on line %d
++bool(false)
++Received Ping!
+diff --git a/ext/pthreads/tests/socket-setoption.phpt b/ext/pthreads/tests/socket-setoption.phpt
+new file mode 100644
+index 00000000..0ec711ea
+--- /dev/null
++++ b/ext/pthreads/tests/socket-setoption.phpt
+@@ -0,0 +1,20 @@
++--TEST--
++Test of Socket::setOption() with and without parameters
++--FILE--
++<?php
++    $socket = new Socket(\Socket::AF_INET, \Socket::SOCK_STREAM, \Socket::SOL_TCP);
++    $socket->setOption();
++
++    try {
++        $socket->setOption("hello", "world", new stdClass());
++    } catch(Throwable $throwable) {
++        var_dump($throwable->getMessage());
++    }
++    var_dump($socket->setOption(\Socket::SOL_SOCKET, \Socket::SO_REUSEADDR, 1));
++    var_dump($socket->getOption(\Socket::SOL_SOCKET, \Socket::SO_REUSEADDR));
++?>
++--EXPECTF--
++Warning: Socket::setOption() expects exactly 3 parameters, 0 given in %s on line %i
++string(%i) "Argument 1 passed to Socket::setOption() must be of the type %s, string given"
++bool(true)
++int(1)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/socket-unixloop.phpt b/ext/pthreads/tests/socket-unixloop.phpt
+new file mode 100644
+index 00000000..f9a72fbc
+--- /dev/null
++++ b/ext/pthreads/tests/socket-unixloop.phpt
+@@ -0,0 +1,56 @@
++--TEST--
++Unix domain socket Loopback test
++--SKIPIF--
++<?php
++if (substr(PHP_OS, 0, 3) == 'WIN') {
++	die('skip.. Not valid for Windows');
++}
++?>
++--FILE--
++<?php
++	$sock_path = sprintf("/tmp/%s.sock", uniqid());
++
++    if (file_exists($sock_path))
++        die('Temporary socket already exists.');
++
++    /* Setup socket server */
++    $server = new Socket(\Socket::AF_UNIX, \Socket::SOCK_STREAM, 0);
++    if (!$server) {
++        die('Unable to create AF_UNIX socket [server]');
++    }
++    if (!$server->bind($sock_path)) {
++        die("Unable to bind to $sock_path");
++    }
++    if (!$server->listen(2)) {
++        die('Unable to listen on socket');
++    }
++
++    /* Connect to it */
++    $client = new Socket(\Socket::AF_UNIX, \Socket::SOCK_STREAM, 0);
++    if (!$client) {
++        die('Unable to create AF_UNIX socket [client]');
++    }
++    if (!$client->connect($sock_path)) {
++        die('Unable to connect to server socket');
++    }
++
++    /* Accept that connection */
++    /** @var Socket $socket */
++    $socket = $server->accept(\Socket::class);
++    if (!$socket) {
++        die('Unable to accept connection');
++    }
++
++    $client->write("ABCdef123\n");
++
++    $data = $socket->read(10, 0, \Socket::BINARY_READ);
++    var_dump($data);
++
++    $client->close();
++    $socket->close();
++    $server->close();
++    @unlink($sock_path);
++?>
++--EXPECT--
++string(10) "ABCdef123
++"
+diff --git a/ext/pthreads/tests/stateful-fatalities.phpt b/ext/pthreads/tests/stateful-fatalities.phpt
+new file mode 100644
+index 00000000..5e958d9f
+--- /dev/null
++++ b/ext/pthreads/tests/stateful-fatalities.phpt
+@@ -0,0 +1,24 @@
++--TEST--
++Test stateful fatalities
++--DESCRIPTION--
++This test verifies that state includes fatalities
++--FILE--
++<?php
++class TestThread extends Thread {
++	public function run(){
++		i_do_not_exist();
++	}
++}
++$test = new TestThread();
++$test->start();
++$test->join();
++var_dump($test->isTerminated());
++?>
++--EXPECTF--
++Fatal error: Uncaught Error: Call to undefined function i_do_not_exist() in %s:4
++Stack trace:
++#0 [internal function]: TestThread->run()
++#1 {main}
++  thrown in %s on line 4
++bool(true)
++
+diff --git a/ext/pthreads/tests/static-inheritance-unexpected-null.phpt b/ext/pthreads/tests/static-inheritance-unexpected-null.phpt
+new file mode 100644
+index 00000000..98891a65
+--- /dev/null
++++ b/ext/pthreads/tests/static-inheritance-unexpected-null.phpt
+@@ -0,0 +1,28 @@
++--TEST--
++Testing gh bug #585 (Static class member nulled after class extends)
++--FILE--
++<?php
++class A{
++    public static $var = 'A';
++
++    public function gVar(){
++        return static::$var;
++    }
++}
++
++class B extends A{
++    public static $var = 'B';
++}
++
++class thr extends Thread{
++    public function run(){
++        $a = new A();
++        var_dump($a->gVar());
++    }
++}
++
++$thr = new thr();
++$thr->start();
++$thr->join();
++--EXPECT--
++string(1) "A"
+diff --git a/ext/pthreads/tests/static-resolution.phpt b/ext/pthreads/tests/static-resolution.phpt
+new file mode 100644
+index 00000000..9fdd927d
+--- /dev/null
++++ b/ext/pthreads/tests/static-resolution.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Test static:: regression
++--DESCRIPTION--
++Bug #210 shows static:: requires different logic to self::
++--FILE--
++<?php
++class testbug extends Thread
++{
++    public static $somevar = 123;
++    
++    function __construct()
++    {
++        var_dump(self::$somevar); 
++        var_dump(static::$somevar);
++    }
++
++    public function run()
++    {
++        var_dump(self::$somevar);
++        var_dump(static::$somevar);
++    }
++}
++
++$testbug = new testbug;
++$testbug->start();
++?>
++--EXPECT--
++int(123)
++int(123)
++int(123)
++int(123)
+diff --git a/ext/pthreads/tests/static-scope-vars.phpt b/ext/pthreads/tests/static-scope-vars.phpt
+new file mode 100644
+index 00000000..798f6f52
+--- /dev/null
++++ b/ext/pthreads/tests/static-scope-vars.phpt
+@@ -0,0 +1,53 @@
++--TEST--
++Test static scope vars gh issue #501
++--DESCRIPTION--
++Static member reference vars were not copied appropriately
++--FILE--
++<?php
++class Test extends Threaded
++{
++    public function method()
++    {
++        static $string = 'value';
++		static $int = 1;
++		static $array = [1,2,3];
++
++        var_dump($string, $int, $array);
++    }
++}
++$objInstance = new Test();
++$objInstance->method();
++
++$objThread = new class extends Thread
++{
++    public function run()
++    {
++        $objInstance = new Test();
++        $objInstance->method();
++    }
++};
++
++$objThread->start();
++$objThread->join();
++--EXPECT--
++string(5) "value"
++int(1)
++array(3) {
++  [0]=>
++  int(1)
++  [1]=>
++  int(2)
++  [2]=>
++  int(3)
++}
++string(5) "value"
++int(1)
++array(3) {
++  [0]=>
++  int(1)
++  [1]=>
++  int(2)
++  [2]=>
++  int(3)
++}
++
+diff --git a/ext/pthreads/tests/staticmethods.phpt b/ext/pthreads/tests/staticmethods.phpt
+new file mode 100644
+index 00000000..68909d63
+--- /dev/null
++++ b/ext/pthreads/tests/staticmethods.phpt
+@@ -0,0 +1,22 @@
++--TEST--
++Test access to static methods from within user threads
++--DESCRIPTION--
++Static methods as declared in the users implementation of Thread should now be available for calling in the thread scope
++--FILE--
++<?php
++class ThreadTest extends Thread {
++	public static function staticTest(){
++		return 1;
++	}
++	
++	public function run(){
++		$this->result = self::staticTest();
++	}
++}
++$thread = new ThreadTest();
++if($thread->start())
++	if ($thread->join())
++		var_dump($thread->result);
++?>
++--EXPECT--
++int(1)
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/statics-thorn-in-other-side.phpt b/ext/pthreads/tests/statics-thorn-in-other-side.phpt
+new file mode 100644
+index 00000000..a9d8f7b8
+--- /dev/null
++++ b/ext/pthreads/tests/statics-thorn-in-other-side.phpt
+@@ -0,0 +1,58 @@
++--TEST--
++Test statics aren't getting fucked with when starting new threads (many bugs)
++--FILE--
++<?php
++class StaticClass{
++    public static $list = array();
++    public static $test;
++    public static $testObject;
++
++    public function __construct(){
++        self::$list[] = $this;
++        self::$test = "randomvalue";
++    }
++}
++
++class ThreadClass extends Thread{   
++
++}
++
++new StaticClass;
++StaticClass::$testObject = new StaticClass;
++echo "BEFORE:\n";
++var_dump(StaticClass::$list, StaticClass::$test, StaticClass::$testObject);
++echo "\n";
++
++$thread = new ThreadClass;
++$thread->start();
++
++echo "AFTER\n";
++var_dump(StaticClass::$list, StaticClass::$test, StaticClass::$testObject);
++echo "\n";
++--EXPECTF--
++BEFORE:
++array(2) {
++  [0]=>
++  object(StaticClass)#1 (0) {
++  }
++  [1]=>
++  object(StaticClass)#2 (0) {
++  }
++}
++string(11) "randomvalue"
++object(StaticClass)#2 (0) {
++}
++
++AFTER
++array(2) {
++  [0]=>
++  object(StaticClass)#1 (0) {
++  }
++  [1]=>
++  object(StaticClass)#2 (0) {
++  }
++}
++string(11) "randomvalue"
++object(StaticClass)#2 (0) {
++}
++
+diff --git a/ext/pthreads/tests/statics.phpt b/ext/pthreads/tests/statics.phpt
+new file mode 100644
+index 00000000..433a0577
+--- /dev/null
++++ b/ext/pthreads/tests/statics.phpt
+@@ -0,0 +1,17 @@
++--TEST--
++Test statics (bug 19)
++--DESCRIPTION--
++This test verifies that static members in declarations made outside of threads are available inside threads without error
++--FILE--
++<?php
++class TestThread extends Thread {
++	static $static = "pthreads rocks!";
++
++	public function run() { var_dump(self::$static); }
++}
++
++$thread = new TestThread();
++$thread->start();
++?>
++--EXPECT--
++string(15) "pthreads rocks!"
+diff --git a/ext/pthreads/tests/synchronization.phpt b/ext/pthreads/tests/synchronization.phpt
+new file mode 100644
+index 00000000..e72ded07
+--- /dev/null
++++ b/ext/pthreads/tests/synchronization.phpt
+@@ -0,0 +1,25 @@
++--TEST--
++Test synchronized blocks
++--DESCRIPTION--
++This test verifies that syncronization is working
++--FILE--
++<?php
++class T extends Thread {
++        public $data;
++        public function run() {
++            $this->synchronized(function($thread){
++				$thread->data = true;
++				$thread->notify();
++			}, $this);               
++        }
++}
++$t = new T;
++$t->start();
++$t->synchronized(function($thread){
++	if (!$thread->data) {
++		var_dump($thread->wait());
++	} else var_dump($thread->data);
++}, $t);
++?>
++--EXPECT--
++bool(true)
+diff --git a/ext/pthreads/tests/thorn-in-side.phpt b/ext/pthreads/tests/thorn-in-side.phpt
+new file mode 100644
+index 00000000..4bfdb249
+--- /dev/null
++++ b/ext/pthreads/tests/thorn-in-side.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Testing thorn in my side hasn't returned
++--DESCRIPTION--
++This test verifies my nemisis has not returned (bug #48)
++--FILE--
++<?php
++class parentClass {
++    private $var;
++
++    public function __construct() {
++        echo $this->var;
++    }
++}
++
++class childClass extends parentClass {
++
++}
++
++class clientThread extends Thread {
++
++    public function run() {
++        $objChild = new childClass();
++
++    }               
++
++}
++
++
++$objClientThread = new clientThread();
++$objClientThread->start();
++--EXPECT--
+diff --git a/ext/pthreads/tests/thread.phpt b/ext/pthreads/tests/thread.phpt
+new file mode 100644
+index 00000000..c33af7fc
+--- /dev/null
++++ b/ext/pthreads/tests/thread.phpt
+@@ -0,0 +1,17 @@
++--TEST--
++Test basic threading
++--DESCRIPTION--
++This test will create and join a simple thread
++--FILE--
++<?php
++class ThreadTest extends Thread {
++	public function run(){
++		/* nothing to do */
++	}
++}
++$thread = new ThreadTest();
++if($thread->start())
++	var_dump($thread->join());
++?>
++--EXPECT--
++bool(true)
+diff --git a/ext/pthreads/tests/threaded-extend-abstract.phpt b/ext/pthreads/tests/threaded-extend-abstract.phpt
+new file mode 100644
+index 00000000..945b5d53
+--- /dev/null
++++ b/ext/pthreads/tests/threaded-extend-abstract.phpt
+@@ -0,0 +1,10 @@
++--TEST--
++Test extending abstract
++--DESCRIPTION--
++This is regression test for #409
++--FILE--
++<?php
++var_dump(Threaded::extend(ReflectionFunctionAbstract::class));
++?>
++--EXPECT--
++bool(true)
+diff --git a/ext/pthreads/tests/threaded_valid_tsrmls.phpt b/ext/pthreads/tests/threaded_valid_tsrmls.phpt
+new file mode 100644
+index 00000000..2c6201e3
+--- /dev/null
++++ b/ext/pthreads/tests/threaded_valid_tsrmls.phpt
+@@ -0,0 +1,36 @@
++--TEST--
++Test correct TSRMLS per threaded object
++--DESCRIPTION--
++This test verifies that TSRMLS is set properly to threaded objects
++--FILE--
++<?php
++class Test extends Threaded
++{
++	private $prop;
++}
++
++$thread = new class extends Thread {
++	public function __construct()
++	{
++		$this->test = new Test();
++		$this->alive = true;
++	}
++
++	public function run()
++	{
++		$this[] = $this->test;
++		while ($this->alive);
++	}
++};
++$thread->start();
++
++while (true) {
++	if (isset($thread[0])) {
++		$thread[0][] = new Threaded();
++
++		if(count($thread[0]) > 32) break;
++	}
++}
++$thread->alive = false;
++$thread->join();
++--EXPECTF--
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/trait-alias-bug.phpt b/ext/pthreads/tests/trait-alias-bug.phpt
+new file mode 100644
+index 00000000..74a7c07b
+--- /dev/null
++++ b/ext/pthreads/tests/trait-alias-bug.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Test pthreads connections
++--DESCRIPTION--
++This test verifies that variables are bound properly by pthreads
++--FILE--
++<?php
++
++trait A {
++  protected function x() {}
++}
++
++class B {
++  use A {
++    x as public;
++  }
++}
++
++class My extends Thread{
++    function run(){
++        for($i=1;$i<2;$i++){
++            echo Thread::getCurrentThreadId();
++        }
++    }
++}
++
++$a = new My();
++$a->start();
++$a->join();
++--EXPECTF--
++%d
++
+diff --git a/ext/pthreads/tests/trait-aliases-reflection.phpt b/ext/pthreads/tests/trait-aliases-reflection.phpt
+new file mode 100644
+index 00000000..dd09dc60
+--- /dev/null
++++ b/ext/pthreads/tests/trait-aliases-reflection.phpt
+@@ -0,0 +1,28 @@
++--TEST--
++Test trait aliases reflection
++--DESCRIPTION--
++Trait aliases not properly copied
++--FILE--
++<?php
++trait Hello {
++    public function world():void { }
++}
++
++class Foo {
++    use Hello { world as sun; }
++}
++$t = new class extends Thread {
++    public function run() {
++        $foo = new Foo();
++
++        $class = new ReflectionClass($foo);
++        var_dump($class->getTraitAliases());
++    }
++};
++$t->start() && $t->join();
++--EXPECT--
++array(1) {
++  ["sun"]=>
++  string(12) "Hello::world"
++}
++
+diff --git a/ext/pthreads/tests/trait-aliases.phpt b/ext/pthreads/tests/trait-aliases.phpt
+new file mode 100644
+index 00000000..43832c33
+--- /dev/null
++++ b/ext/pthreads/tests/trait-aliases.phpt
+@@ -0,0 +1,26 @@
++--TEST--
++Test trait aliases
++--DESCRIPTION--
++Fix bug #274, trait aliases causing memory errors
++--FILE--
++<?php
++trait testTrait {
++    public function fromTrait ($foo) {
++        var_dump(__FUNCTION__);
++    }
++}
++
++class myThread extends Thread {
++    use testTrait  {fromTrait as fromTraitAliased;}
++
++    public function run() {
++        $this->fromTrait('blah');
++    }
++}
++
++$t = new myThread();
++$t->start();
++$t->join();
++--EXPECT--
++string(9) "fromTrait"
++
+diff --git a/ext/pthreads/tests/traits-scope.inc b/ext/pthreads/tests/traits-scope.inc
+new file mode 100644
+index 00000000..f903b8e5
+--- /dev/null
++++ b/ext/pthreads/tests/traits-scope.inc
+@@ -0,0 +1,10 @@
++<?php
++class MyClass {
++	use \MyTrait;
++
++	public function doSomething() {
++		$this->getSomething(true);
++		printf("OK\n");
++	}
++}
++
+diff --git a/ext/pthreads/tests/traits-scope.phpt b/ext/pthreads/tests/traits-scope.phpt
+new file mode 100644
+index 00000000..b6a1d0ae
+--- /dev/null
++++ b/ext/pthreads/tests/traits-scope.phpt
+@@ -0,0 +1,27 @@
++--TEST--
++Test traits scope (gh issue #484)
++--FILE--
++<?php
++class MyWork extends \Threaded {
++	use \MyTrait;
++
++	public function run() {
++    	$this->getSomething();
++	}
++}
++
++trait MyTrait {
++	protected function getSomething($test = false) {
++		if (!$test) {
++			require_once 'traits-scope.inc';
++			return (new \MyClass())->doSomething();
++		}
++	}
++}
++
++$pool = new \Pool(1, \Worker::class);
++$pool->submit(new MyWork());
++$pool->shutdown();
++--EXPECT--
++OK
++
+diff --git a/ext/pthreads/tests/unprepared-entry-static-segfault.phpt b/ext/pthreads/tests/unprepared-entry-static-segfault.phpt
+new file mode 100644
+index 00000000..a440f995
+--- /dev/null
++++ b/ext/pthreads/tests/unprepared-entry-static-segfault.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Test fix for #665
++--DESCRIPTION--
++Unprepared entries in static properties causing segfault
++--FILE--
++<?php
++class SystemLoader extends \Threaded
++{
++    private static $objConfig = null;
++
++    public static function getConfig()
++    {
++        self::$objConfig = new SystemLoaderConfig();
++    }
++}
++
++class SystemLoaderConfig extends \Threaded { }
++
++SystemLoader::getConfig();
++
++class Test extends Thread {
++	public function run(){
++		echo SystemLoaderConfig::class;
++	}
++}
++
++$objTestThread = new Test();
++$objTestThread->start();
++$objTestThread->join();
++--EXPECT--
++SystemLoaderConfig
+diff --git a/ext/pthreads/tests/unset-defaults.phpt b/ext/pthreads/tests/unset-defaults.phpt
+new file mode 100644
+index 00000000..940a67e9
+--- /dev/null
++++ b/ext/pthreads/tests/unset-defaults.phpt
+@@ -0,0 +1,19 @@
++--TEST--
++Test unset defaults [moot: default values are ignored]
++--DESCRIPTION--
++This test verifies that unset members do not cause a problem in pthreads objects
++--FILE--
++<?php
++class TestThread extends Thread {
++	public $default;
++	
++	public function run() {
++		var_dump($this->default); 
++	}
++}
++
++$thread = new TestThread();
++$thread->start();
++?>
++--EXPECT--
++NULL
+diff --git a/ext/pthreads/tests/unstack-running.phpt b/ext/pthreads/tests/unstack-running.phpt
+new file mode 100644
+index 00000000..3f082c06
+--- /dev/null
++++ b/ext/pthreads/tests/unstack-running.phpt
+@@ -0,0 +1,27 @@
++--TEST--
++Test Worker::unstack while a task is currently executing
++--DESCRIPTION--
++Unstacking a task would cause it to be freed from the worker stack, but a currently-executing task would not be told not to point to the destroyed task, resulting in an attempted double-free when collecting garbage from workers
++--FILE--
++<?php
++$w = new Worker();
++$w->start();
++
++class Task extends Threaded{
++    public function run() : void{
++        sleep(1);
++    }
++}
++
++for($i = 0; $i < 2; ++$i){
++    $w->stack(new Task);
++}
++
++usleep(500000);
++$w->unstack();
++$w->shutdown();
++
++var_dump("ok");
++?>
++--EXPECTF--
++string(2) "ok"
+diff --git a/ext/pthreads/tests/verify-overload.phpt b/ext/pthreads/tests/verify-overload.phpt
+new file mode 100644
+index 00000000..9e2a185a
+--- /dev/null
++++ b/ext/pthreads/tests/verify-overload.phpt
+@@ -0,0 +1,53 @@
++--TEST--
++Test VERIFY_RETURN_TYPE overload
++--DESCRIPTION--
++When using the explicit cast of objects, return type hinting can be broken.
++
++The class in the current context doesn't match the class of the object.
++
++pthreads overrides ZEND_VERIFY_RETURN_TYPE to fix the problem, nothing else is affected.
++--FILE--
++<?php
++class Custom extends Threaded {}
++
++class Test extends Thread {
++
++	public function __construct(Custom $custom) {
++		$this->custom = $custom;
++	}
++
++	public function method() : Custom {
++		return (object) $this->custom;
++	}
++
++	public function checkReturnType():int {
++		return 1;
++	}
++
++	public function wrongReturnType():string {
++		return 1;
++	}
++
++	public function checkOptionalNull():?Threaded {
++		return ($var = null);
++	}
++
++	public function run() {
++		var_dump($this->method());
++		var_dump($this->checkReturnType());
++		var_dump($this->wrongReturnType());
++		var_dump($this->checkOptionalNull());
++	}
++}
++
++$custom = new Custom();
++$test = new Test($custom);
++$test->start() && $test->join();
++?>
++--EXPECT--
++object(Custom)#1 (0) {
++}
++int(1)
++string(1) "1"
++NULL
++
+diff --git a/ext/pthreads/tests/volatile-arrays.phpt b/ext/pthreads/tests/volatile-arrays.phpt
+new file mode 100644
+index 00000000..a1afbfd6
+--- /dev/null
++++ b/ext/pthreads/tests/volatile-arrays.phpt
+@@ -0,0 +1,99 @@
++--TEST--
++Test Volatile Arrays
++--DESCRIPTION--
++Arrays were difficult to use in pthreads, their behaviour was strange and inconsistent with Zend arrays.
++
++pthreads v3 will coerce arrays to Volatile objects when they are set as members of Threaded objects.
++
++Threaded objects have been made consistent with PHP arrays, so there should be no noticable difference between an array
++and a volatile object.
++--FILE--
++<?php
++$threaded = new Threaded();
++
++$threaded->test = [
++	"greeting" => "Hello World", 
++	"child" => [
++		"of" => "mine",
++		"grandchild" => [
++			"of" => "parents"
++		]
++	]
++];
++
++/*
++ This looks strange, but needs to be consistent with zend, so we'll test here ... 
++*/
++$threaded["0"] = [];
++$threaded[1] = [];
++$threaded[2] = 'foo';
++$threaded['3'] = 'bar';
++
++var_dump($threaded);
++var_dump(isset($threaded["0"]));
++var_dump(isset($threaded[0]));
++
++unset($threaded["2"], $threaded[3]);
++
++/*
++ This kind of thing would simply fail before, creating really unexpected results
++*/
++$threaded->test["child"]["of"] = "yours";
++$threaded->test["child"]["grandchild"]["of"] = "devil";
++
++var_dump($threaded);
++?>
++--EXPECTF--
++object(Threaded)#%d (%d) {
++  ["test"]=>
++  object(Volatile)#%d (%d) {
++    ["greeting"]=>
++    string(11) "Hello World"
++    ["child"]=>
++    object(Volatile)#%d (%d) {
++      ["of"]=>
++      string(4) "mine"
++      ["grandchild"]=>
++      object(Volatile)#%d (%d) {
++        ["of"]=>
++        string(7) "parents"
++      }
++    }
++  }
++  [0]=>
++  object(Volatile)#%d (%d) {
++  }
++  [1]=>
++  object(Volatile)#%d (%d) {
++  }
++  [2]=>
++  string(3) "foo"
++  [3]=>
++  string(3) "bar"
++}
++bool(true)
++bool(true)
++object(Threaded)#%d (%d) {
++  ["test"]=>
++  object(Volatile)#%d (%d) {
++    ["greeting"]=>
++    string(11) "Hello World"
++    ["child"]=>
++    object(Volatile)#%d (%d) {
++      ["of"]=>
++      string(5) "yours"
++      ["grandchild"]=>
++      object(Volatile)#%d (%d) {
++        ["of"]=>
++        string(5) "devil"
++      }
++    }
++  }
++  [0]=>
++  object(Volatile)#%d (%d) {
++  }
++  [1]=>
++  object(Volatile)#%d (%d) {
++  }
++}
++
+diff --git a/ext/pthreads/tests/volatile-nested.phpt b/ext/pthreads/tests/volatile-nested.phpt
+new file mode 100644
+index 00000000..bcee47c9
+--- /dev/null
++++ b/ext/pthreads/tests/volatile-nested.phpt
+@@ -0,0 +1,90 @@
++--TEST--
++Test nested volatile variables
++--DESCRIPTION--
++This test verifies the possibility to nest volatile variables
++--FILE--
++<?php
++class Node extends Volatile {}
++
++class TestNestedWrite extends Thread {
++    private $shared;
++
++    public function __construct(Volatile $shared) {
++        $this->shared = $shared;
++    }
++
++    public function run() {
++        var_dump($this->shared['queue'][0]);
++
++        // link to new var
++        $this->shared['queue'][1] = $this->shared['queue'][0];
++
++        // unset old ref
++        //unset($this->shared['queue'][0]);
++
++        // or replace ref
++        $this->shared['queue'][0] = new Volatile();
++
++        $this->shared['lock'] = true;
++
++        while(!isset($this->shared['lock2'])) {}
++
++        var_dump($this->shared['queue'][1]);
++
++        $this->shared['lock3'] = true;
++    }
++}
++
++class TestNestedRead extends Thread {
++    private $shared;
++
++    public function __construct(Volatile $shared) {
++        $this->shared = $shared;
++    }
++
++    public function run() {
++        while(!isset($this->shared['lock']));
++
++        var_dump($this->shared['queue'][1]);
++
++        $this->shared['queue'][1] = new Node();
++        $this->shared['lock2'] = true;
++
++        while(!isset($this->shared['quit']));
++    }
++}
++
++class Test extends Thread {
++    public function run() {
++        $queue = new Volatile();
++        $queue[0] = new Volatile();
++
++        $shared = new Volatile();
++        $shared['queue'] = $queue;
++
++        $thread = new TestNestedWrite($shared);
++        $thread->start();
++
++        $thread2 = new TestNestedRead($shared);
++        $thread2->start();
++
++        while(!isset($shared['lock3']));
++
++        $shared['quit'] = true;
++
++        $thread2->join();
++        $thread->join();
++    }
++}
++$thread = new Test();
++$thread->start();
++$thread->join();
++?>
++--EXPECT--
++object(Volatile)#4 (0) {
++}
++object(Volatile)#4 (0) {
++}
++object(Node)#6 (0) {
++}
++
+diff --git a/ext/pthreads/tests/volatile-objects.phpt b/ext/pthreads/tests/volatile-objects.phpt
+new file mode 100644
+index 00000000..8b3e01d6
+--- /dev/null
++++ b/ext/pthreads/tests/volatile-objects.phpt
+@@ -0,0 +1,70 @@
++--TEST--
++Test Volatile objects
++--DESCRIPTION--
++Threaded members of Threaded objects are immutable, for the sake of performance and ease of use.
++
++Volatile objects' members are excempt from immutability, but can still be an immutable property.
++
++This test both demonstrates and tests volatility and immutability.
++--FILE--
++<?php
++class Member extends Volatile {
++
++	public function method(array $thing) {
++		return $this->thing = $thing; 			# fine, coerced to volatile ...
++	}
++
++	public function setThreaded() {
++		$this->threaded = new Threaded();		# fine, volatile, threaded without refs, still works
++		$this->threaded->merge($this->thing); 	# fine, but expensive
++	}
++
++	public function condition() {
++		return true;
++	}
++}
++
++class Test extends Thread {
++
++	public function __construct(Member $member) {
++		$this->member = $member; 				# immutable, Volatile is still Threaded and this is a normal
++												# Threaded object (Thread extends Threaded)
++	}
++
++	public function run() {
++		$members = [];
++		if ($this->member->condition()) {		# calls to Volatile object methods still fast for this object
++			$this->member
++				->method([1,2,3,4,5]);
++			var_dump($this->member->thing); 	# fine will display ^Threaded
++			$this->member->setThreaded(); 		# fine not immutable because volatile
++
++			$this->member = new Threaded(); 	# not fine, immutable		
++		}
++	}
++}
++
++$member = new Member();
++$test = new Test($member);
++$test->start();
++$test->join();
++?>
++--EXPECTF--
++object(Volatile)#%d (%d) {
++  [0]=>
++  int(1)
++  [1]=>
++  int(2)
++  [2]=>
++  int(3)
++  [3]=>
++  int(4)
++  [4]=>
++  int(5)
++}
++
++Fatal error: Uncaught RuntimeException: Threaded members previously set to Threaded objects are immutable, cannot overwrite member in %s:33
++Stack trace:
++#0 [internal function]: Test->run()
++#1 {main}
++  thrown in %s on line 33
+diff --git a/ext/pthreads/tests/wait.phpt b/ext/pthreads/tests/wait.phpt
+new file mode 100644
+index 00000000..b7d3451f
+--- /dev/null
++++ b/ext/pthreads/tests/wait.phpt
+@@ -0,0 +1,32 @@
++--TEST--
++Test wait/notify
++--DESCRIPTION--
++This test will verify wait/notify functionality
++--FILE--
++<?php
++class ThreadTest extends Thread {
++	public $sent;
++	
++	public function __construct() {
++	    $this->sent = false;
++	}
++	
++	public function run(){
++	    $this->synchronized(function($self){
++		    $self->sent = true;
++		    $self->notify();
++	    }, $this);
++	}
++}
++$thread = new ThreadTest();
++if($thread->start()) {
++	$thread->synchronized(function($me){
++	    if (!$me->sent) {
++		    var_dump($me->wait());
++	    } else var_dump($me->sent);
++	}, $thread);
++	
++} else printf("bool(false)\n");
++?>
++--EXPECT--
++bool(true)
+diff --git a/ext/pthreads/tests/waiting-timeouts.phpt b/ext/pthreads/tests/waiting-timeouts.phpt
+new file mode 100644
+index 00000000..075d645d
+--- /dev/null
++++ b/ext/pthreads/tests/waiting-timeouts.phpt
+@@ -0,0 +1,35 @@
++--TEST--
++Test waiting timeouts
++--DESCRIPTION--
++This test verifies that reaching a timeout returns the correct value
++
++Note that this is a dodgy test, ONLY EVER WAIT FOR SOMETHING
++We assume, wrongly, that the thread will be active and waiting within 3
++seconds.
++--SKIPIF--
++<?php if (defined('PHP_WINDOWS_VERSION_MAJOR')) die("skip: no support for this on windows"); ?>
++--FILE--
++<?php
++class T extends Thread {
++        public $data;
++        public function run() {
++			$start = time();
++            $this->synchronized(function() use($start) {
++				while (time() - $start < 3) {
++					$this->wait(100000);
++				}
++			});
++        }
++}
++
++$t = new T;
++$t->start();
++$t->synchronized(function($thread){
++	var_dump($thread->wait(100)); # should return false because no notification sent
++								  # but may wake up (and return true) because notification might come from
++								  # somewhere else in the system and there is no precondition
++								  # we are testing for faults rather than output here ...
++}, $t);
++?>
++--EXPECTF--
++bool(%s)
+diff --git a/ext/pthreads/tests/workers-constant-copy.phpt b/ext/pthreads/tests/workers-constant-copy.phpt
+new file mode 100644
+index 00000000..191e57c0
+--- /dev/null
++++ b/ext/pthreads/tests/workers-constant-copy.phpt
+@@ -0,0 +1,24 @@
++--TEST--
++Test constants copying in worker
++--DESCRIPTION--
++Constants copying in worker results in endless loop and crashes
++--FILE--
++<?php
++
++$worker = new Worker();
++$worker->start(PTHREADS_INHERIT_NONE);
++$worker->stack(new Testing());
++$worker->shutdown();
++
++class Testing extends Threaded
++{
++    const MY_VAR = "Testing";
++
++    public function run()
++    {
++        var_dump("Running...");
++    }
++}
++?>
++--EXPECTF--
++string(10) "Running..."
+\ No newline at end of file
+diff --git a/ext/pthreads/tests/workers-default-collector.phpt b/ext/pthreads/tests/workers-default-collector.phpt
+new file mode 100644
+index 00000000..18fb8d1b
+--- /dev/null
++++ b/ext/pthreads/tests/workers-default-collector.phpt
+@@ -0,0 +1,33 @@
++--TEST--
++Test pthreads workers default collector
++--DESCRIPTION--
++This test verifies that the default collector works as expected
++--FILE--
++<?php
++$worker = new Worker();
++$worker->start();
++
++$i = 0;
++while ($i<10) {
++	$worker->stack(new class extends Threaded {
++		public function run() {
++			$this->g = true;
++		}
++		
++		public function isGarbage() : bool {
++			return $this->g;
++		}
++
++		public $g = false;
++	});
++	$i++;
++}
++
++var_dump($i);
++while ($worker->collect()) continue;
++var_dump($worker->getStacked());
++$worker->shutdown();
++?>
++--EXPECTF--
++int(10)
++int(0)
+diff --git a/ext/pthreads/tests/workers-no-collect.phpt b/ext/pthreads/tests/workers-no-collect.phpt
+new file mode 100644
+index 00000000..e1c38eb4
+--- /dev/null
++++ b/ext/pthreads/tests/workers-no-collect.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Test pthreads workers rules (collect)
++--DESCRIPTION--
++This test verifies that workers cannot be misused (collect)
++--FILE--
++<?php
++class Test extends Thread {
++	public function __construct(Worker $worker) {
++		$this->worker = $worker;
++	}
++	
++	public function run() {
++		$this->worker->collect(function($t){
++			return $t->isGarbage();
++		});
++	}
++}
++
++$worker = new Worker();
++$worker->start();
++$test = new Test($worker);
++$test->start();
++$test->join();
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: only the creator of this Worker may call collect in %s:10
++Stack trace:
++#0 %s(10): Worker->collect(Object(Closure))
++#1 [internal function]: Test->run()
++#2 {main}
++  thrown in %s on line 10
+diff --git a/ext/pthreads/tests/workers-no-join.phpt b/ext/pthreads/tests/workers-no-join.phpt
+new file mode 100644
+index 00000000..58fe128f
+--- /dev/null
++++ b/ext/pthreads/tests/workers-no-join.phpt
+@@ -0,0 +1,31 @@
++--TEST--
++Test pthreads workers rules (join)
++--DESCRIPTION--
++This test verifies that workers cannot be misused (join)
++--FILE--
++<?php
++class Test extends Thread {
++	public function __construct(Worker $worker) {
++		$this->worker = $worker;
++	}
++	
++	public function run() {
++		$this->worker->shutdown();
++	}
++}
++
++$worker = new Worker();
++$worker->start();
++$test = new Test($worker);
++$test->start();
++$test->join();
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: only the creator of this Worker may join with it in %s:8
++Stack trace:
++#0 %s(8): Worker->shutdown()
++#1 [internal function]: Test->run()
++#2 {main}
++  thrown in %s on line 8
++
++
+diff --git a/ext/pthreads/tests/workers-no-shutdown-before-start.phpt b/ext/pthreads/tests/workers-no-shutdown-before-start.phpt
+new file mode 100644
+index 00000000..e12d7f0c
+--- /dev/null
++++ b/ext/pthreads/tests/workers-no-shutdown-before-start.phpt
+@@ -0,0 +1,22 @@
++--TEST--
++Test pthreads Worker::shutdown
++--DESCRIPTION--
++This test verifies shutdown of a Worker (or Thread) not yet started doesn't fault
++--FILE--
++<?php
++$worker = new Worker();
++
++$worker->stack(new class extends Threaded {
++	public function run() {
++		var_dump($this);
++	}
++});
++
++$worker->shutdown();
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: Worker has not been started in %s:10
++Stack trace:
++#0 %s(10): Worker->shutdown()
++#1 {main}
++  thrown in %s on line 10
+diff --git a/ext/pthreads/tests/workers-no-stack.phpt b/ext/pthreads/tests/workers-no-stack.phpt
+new file mode 100644
+index 00000000..4fd5037b
+--- /dev/null
++++ b/ext/pthreads/tests/workers-no-stack.phpt
+@@ -0,0 +1,36 @@
++--TEST--
++Test pthreads workers rules (stack)
++--DESCRIPTION--
++This test verifies that workers cannot be misused (stack)
++--FILE--
++<?php
++class Work extends Threaded {
++}
++
++class Test extends Thread {
++	public function __construct(Worker $worker) {
++		$this->worker = $worker;
++	}
++	
++	public function run() {
++		$c = new Work();
++		$this->worker
++			->stack($c);
++	}
++}
++
++$worker = new Worker();
++$worker->start();
++$test = new Test($worker);
++$test->start();
++$test->join();
++$worker->shutdown();
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: only the creator of this Worker may call stack in %s:%d
++Stack trace:
++#0 %s(%d): Worker->stack(Object(Work))
++#1 [internal function]: Test->run()
++#2 {main}
++  thrown in %s on line %d
++
+diff --git a/ext/pthreads/tests/workers-no-start.phpt b/ext/pthreads/tests/workers-no-start.phpt
+new file mode 100644
+index 00000000..b69383c3
+--- /dev/null
++++ b/ext/pthreads/tests/workers-no-start.phpt
+@@ -0,0 +1,30 @@
++--TEST--
++Test pthreads workers rules (start)
++--DESCRIPTION--
++This test verifies that workers cannot be misused (start)
++--FILE--
++<?php
++class Test extends Thread {
++	public function __construct(Worker $worker) {
++		$this->worker = $worker;
++	}
++	
++	public function run() {
++		$this->worker->start();
++	}
++}
++
++$worker = new Worker();
++$test = new Test($worker);
++$test->start();
++$test->join();
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: only the creator of this Worker may start it in %s:8
++Stack trace:
++#0 %s(8): Thread->start()
++#1 [internal function]: Test->run()
++#2 {main}
++  thrown in %s on line 8
++
++
+diff --git a/ext/pthreads/tests/workers-no-unstack.phpt b/ext/pthreads/tests/workers-no-unstack.phpt
+new file mode 100644
+index 00000000..e0f5846a
+--- /dev/null
++++ b/ext/pthreads/tests/workers-no-unstack.phpt
+@@ -0,0 +1,32 @@
++--TEST--
++Test pthreads workers rules (unstack)
++--DESCRIPTION--
++This test verifies that workers cannot be misused (unstack)
++--FILE--
++<?php
++class Test extends Thread {
++	public function __construct(Worker $worker) {
++		$this->worker = $worker;
++	}
++	
++	public function run() {
++		$this->worker->unstack();
++	}
++}
++
++$worker = new Worker();
++$worker->start();
++
++$test = new Test($worker);
++$test->start();
++$test->join();
++?>
++--EXPECTF--
++Fatal error: Uncaught RuntimeException: only the creator of this Worker may call unstack in %s:8
++Stack trace:
++#0 %s(8): Worker->unstack()
++#1 [internal function]: Test->run()
++#2 {main}
++  thrown in %s on line 8
++
++
+diff --git a/ext/pthreads/tests/workers-ok-unstack.phpt b/ext/pthreads/tests/workers-ok-unstack.phpt
+new file mode 100644
+index 00000000..96f66c46
+--- /dev/null
++++ b/ext/pthreads/tests/workers-ok-unstack.phpt
+@@ -0,0 +1,19 @@
++--TEST--
++Test pthreads Worker::unstack
++--DESCRIPTION--
++This test verifies that unstack functions as intended
++--FILE--
++<?php
++$worker = new Worker();
++
++$worker->stack(new class extends Threaded {
++	public function run() {
++		var_dump($this);
++	}
++});
++
++var_dump($worker->unstack());
++?>
++--EXPECTF--
++object(class@anonymous)#%d (%d) {
++}
+-- 
+2.20.1
+
diff --git a/patches/series b/patches/series
index 424cb2d..02ab97a 100644
--- a/patches/series
+++ b/patches/series
@@ -46,3 +46,4 @@
 0046-Fix-ext-date-lib-parse_tz-PATH_MAX-HURD-FTBFS.patch
 0047-Use-pkg-config-for-FreeType2-detection.patch
 0048-Amend-C-11-for-intl-compilation-on-older-distributio.patch
+metztli/metztli-ext-pthreads.patch
diff --git a/rules b/rules
index ce87af8..03bda7e 100755
--- a/rules
+++ b/rules
@@ -524,6 +524,7 @@ override_dh_install-arch: remove-files-stamp prepare-fpm-pools
 	cat php.ini-production | tr "\t" " " | \
 	$(SED) -e'/memory_limit =/ s/128M/-1/g;' \
 	    -e'/session.gc_probability =/ s/1/0/g' \
+	    -e'937i;Metztli IT added required cli directive:\nextension=pthreads' \
 	  > debian/$(PHP_COMMON)/usr/lib/php/$(PHP_NAME_VERSION)/php.ini-production.cli
 
 	cat php.ini-development | tr "\t" " " | \
diff --git a/rules.d/pthreads.mk b/rules.d/pthreads.mk
new file mode 100644
index 0000000..1540311
--- /dev/null
+++ b/rules.d/pthreads.mk
@@ -0,0 +1,10 @@
+ext_PACKAGES      += pthreads
+pthreads_DESCRIPTION := KrakJoe pthreads
+pthreads_EXTENSIONS  := pthreads
+pthreads_PRIORITY    := 10
+pthreads_EXTENSION   := krakjoe_extension
+pthreads_config      := --enable-pthreads=shared,/usr
+export pthreads_EXTENSIONS
+export pthreads_DESCRIPTION
+export pthreads_PRIORITY
+export pthreads_EXTENSION
-- 
2.20.1

