<?php

namespace Illuminate\Session;

use Illuminate\Support\Manager;

/**
 * @mixin \Illuminate\Session\Store
 */
class SessionManager extends Manager
{
    /**
     * Call a custom driver creator.
     *
     * @param  string  $driver
     * @return \Illuminate\Session\Store
     */
    protected function callCustomCreator($driver)
    {
        return $this->buildSession(parent::callCustomCreator($driver));
    }

    /**
     * Create an instance of the "null" session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createNullDriver()
    {
        return $this->buildSession(new NullSessionHandler);
    }

    /**
     * Create an instance of the "array" session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createArrayDriver()
    {
        return $this->buildSession(new ArraySessionHandler(
            $this->config->get('session.lifetime')
        ));
    }

    /**
     * Create an instance of the "cookie" session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createCookieDriver()
    {
        return $this->buildSession(new CookieSessionHandler(
            $this->container->make('cookie'),
            $this->config->get('session.lifetime'),
            $this->config->get('session.expire_on_close')
        ));
    }

    /**
     * Create an instance of the file session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createFileDriver()
    {
        return $this->createNativeDriver();
    }

    /**
     * Create an instance of the file session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createNativeDriver()
    {
        $lifetime = $this->config->get('session.lifetime');

        return $this->buildSession(new FileSessionHandler(
            $this->container->make('files'), $this->config->get('session.files'), $lifetime
        ));
    }

    /**
     * Create an instance of the database session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createDatabaseDriver()
    {
        $table = $this->config->get('session.table');

        $lifetime = $this->config->get('session.lifetime');

        return $this->buildSession(new DatabaseSessionHandler(
            $this->getDatabaseConnection(), $table, $lifetime, $this->container
        ));
    }

    /**
     * Get the database connection for the database driver.
     *
     * @return \Illuminate\Database\Connection
     */
    protected function getDatabaseConnection()
    {
        $connection = $this->config->get('session.connection');

        return $this->container->make('db')->connection($connection);
    }

    /**
     * Create an instance of the APC session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createApcDriver()
    {
        return $this->createCacheBased('apc');
    }

    /**
     * Create an instance of the Memcached session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createMemcachedDriver()
    {
        return $this->createCacheBased('memcached');
    }

    /**
     * Create an instance of the Redis session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createRedisDriver()
    {
        $handler = $this->createCacheHandler('redis');

        $handler->getCache()->getStore()->setConnection(
            $this->config->get('session.connection')
        );

        return $this->buildSession($handler);
    }

    /**
     * Create an instance of the DynamoDB session driver.
     *
     * @return \Illuminate\Session\Store
     */
    protected function createDynamodbDriver()
    {
        return $this->createCacheBased('dynamodb');
    }

    /**
     * Create an instance of a cache driven driver.
     *
     * @param  string  $driver
     * @return \Illuminate\Session\Store
     */
    protected function createCacheBased($driver)
    {
        return $this->buildSession($this->createCacheHandler($driver));
    }

    /**
     * Create the cache based session handler instance.
     *
     * @param  string  $driver
     * @return \Illuminate\Session\CacheBasedSessionHandler
     */
    protected function createCacheHandler($driver)
    {
        $store = $this->config->get('session.store') ?: $driver;

        return new CacheBasedSessionHandler(
            clone $this->container->make('cache')->store($store),
            $this->config->get('session.lifetime')
        );
    }

    /**
     * Build the session instance.
     *
     * @param  \SessionHandlerInterface  $handler
     * @return \Illuminate\Session\Store
     */
    protected function buildSession($handler)
    {
        return $this->config->get('session.encrypt')
                ? $this->buildEncryptedSession($handler)
                : new Store(
                    $this->config->get('session.cookie'),
                    $handler,
                    $id = null,
                    $this->config->get('session.serialization', 'php')
                );
    }

    /**
     * Build the encrypted session instance.
     *
     * @param  \SessionHandlerInterface  $handler
     * @return \Illuminate\Session\EncryptedStore
     */
    protected function buildEncryptedSession($handler)
    {
        return new EncryptedStore(
            $this->config->get('session.cookie'),
            $handler,
            $this->container['encrypter'],
            $id = null,
            $this->config->get('session.serialization', 'php'),
        );
    }

    /**
     * Determine if requests for the same session should wait for each to finish before executing.
     *
     * @return bool
     */
    public function shouldBlock()
    {
        return $this->config->get('session.block', false);
    }

    /**
     * Get the name of the cache store / driver that should be used to acquire session locks.
     *
     * @return string|null
     */
    public function blockDriver()
    {
        return $this->config->get('session.block_store');
    }

    /**
     * Get the maximum number of seconds the session lock should be held for.
     *
     * @return int
     */
    public function defaultRouteBlockLockSeconds()
    {
        return $this->config->get('session.block_lock_seconds', 10);
    }

    /**
     * Get the maximum number of seconds to wait while attempting to acquire a route block session lock.
     *
     * @return int
     */
    public function defaultRouteBlockWaitSeconds()
    {
        return $this->config->get('session.block_wait_seconds', 10);
    }

    /**
     * Get the session configuration.
     *
     * @return array
     */
    public function getSessionConfig()
    {
        return $this->config->get('session');
    }

    /**
     * Get the default session driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->config->get('session.driver');
    }

    /**
     * Set the default session driver name.
     *
     * @param  string  $name
     * @return void
     */
    public function setDefaultDriver($name)
    {
        $this->config->set('session.driver', $name);
    }
}


<?php

namespace Illuminate\Session\Middleware;

use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Contracts\Session\Middleware\AuthenticatesSessions;
use Illuminate\Http\Request;

class AuthenticateSession implements AuthenticatesSessions
{
    /**
     * The authentication factory implementation.
     *
     * @var \Illuminate\Contracts\Auth\Factory
     */
    protected $auth;

    /**
     * The callback that should be used to generate the authentication redirect path.
     *
     * @var callable
     */
    protected static $redirectToCallback;

    /**
     * Create a new middleware instance.
     *
     * @param  \Illuminate\Contracts\Auth\Factory  $auth
     * @return void
     */
    public function __construct(AuthFactory $auth)
    {
        $this->auth = $auth;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (! $request->hasSession() || ! $request->user() || ! $request->user()->getAuthPassword()) {
            return $next($request);
        }

        if ($this->guard()->viaRemember()) {
            $passwordHash = explode('|', $request->cookies->get($this->guard()->getRecallerName()))[2] ?? null;

            if (! $passwordHash || ! hash_equals($request->user()->getAuthPassword(), $passwordHash)) {
                $this->logout($request);
            }
        }

        if (! $request->session()->has('password_hash_'.$this->auth->getDefaultDriver())) {
            $this->storePasswordHashInSession($request);
        }

        if (! hash_equals($request->session()->get('password_hash_'.$this->auth->getDefaultDriver()), $request->user()->getAuthPassword())) {
            $this->logout($request);
        }

        return tap($next($request), function () use ($request) {
            if (! is_null($this->guard()->user())) {
                $this->storePasswordHashInSession($request);
            }
        });
    }

    /**
     * Store the user's current password hash in the session.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function storePasswordHashInSession($request)
    {
        if (! $request->user()) {
            return;
        }

        $request->session()->put([
            'password_hash_'.$this->auth->getDefaultDriver() => $request->user()->getAuthPassword(),
        ]);
    }

    /**
     * Log the user out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     *
     * @throws \Illuminate\Auth\AuthenticationException
     */
    protected function logout($request)
    {
        $this->guard()->logoutCurrentDevice();

        $request->session()->flush();

        throw new AuthenticationException(
            'Unauthenticated.', [$this->auth->getDefaultDriver()], $this->redirectTo($request)
        );
    }

    /**
     * Get the guard instance that should be used by the middleware.
     *
     * @return \Illuminate\Contracts\Auth\Factory|\Illuminate\Contracts\Auth\Guard
     */
    protected function guard()
    {
        return $this->auth;
    }

    /**
     * Get the path the user should be redirected to when their session is not authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string|null
     */
    protected function redirectTo(Request $request)
    {
        if (static::$redirectToCallback) {
            return call_user_func(static::$redirectToCallback, $request);
        }
    }

    /**
     * Specify the callback that should be used to generate the redirect path.
     *
     * @param  callable  $redirectToCallback
     * @return void
     */
    public static function redirectUsing(callable $redirectToCallback)
    {
        static::$redirectToCallback = $redirectToCallback;
    }
}


<?php

namespace Illuminate\Session\Console;

use Illuminate\Console\MigrationGeneratorCommand;
use Symfony\Component\Console\Attribute\AsCommand;

use function Illuminate\Filesystem\join_paths;

#[AsCommand(name: 'make:session-table', aliases: ['session:table'])]
class SessionTableCommand extends MigrationGeneratorCommand
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'make:session-table';

    /**
     * The console command name aliases.
     *
     * @var array
     */
    protected $aliases = ['session:table'];

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create a migration for the session database table';

    /**
     * Get the migration table name.
     *
     * @return string
     */
    protected function migrationTableName()
    {
        return 'sessions';
    }

    /**
     * Get the path to the migration stub file.
     *
     * @return string
     */
    protected function migrationStubFile()
    {
        return __DIR__.'/stubs/database.stub';
    }

    /**
     * Determine whether a migration for the table already exists.
     *
     * @param  string  $table
     * @return bool
     */
    protected function migrationExists($table)
    {
        foreach ([
            join_paths($this->laravel->databasePath('migrations'), '*_*_*_*_create_'.$table.'_table.php'),
            join_paths($this->laravel->databasePath('migrations'), '0001_01_01_000000_create_users_table.php'),
        ] as $path) {
            if (count($this->files->glob($path)) !== 0) {
                return true;
            }
        }

        return false;
    }
}


[PHP]

;;;;;;;;;;;;;;;;;;;
; About php.ini   ;
;;;;;;;;;;;;;;;;;;;
; PHP's initialization file, generally called php.ini, is responsible for
; configuring many of the aspects of PHP's behavior.

; PHP attempts to find and load this configuration from a number of locations.
; The following is a summary of its search order:
; 1. SAPI module specific location.
; 2. The PHPRC environment variable.
; 3. A number of predefined registry keys on Windows
; 4. Current working directory (except CLI)
; 5. The web server's directory (for SAPI modules), or directory of PHP
; (otherwise in Windows)
; 6. The directory from the --with-config-file-path compile time option, or the
; Windows directory (usually C:\windows)
; See the PHP docs for more specific information.
; https://php.net/configuration.file

; The syntax of the file is extremely simple.  Whitespace and lines
; beginning with a semicolon are silently ignored (as you probably guessed).
; Section headers (e.g. [Foo]) are also silently ignored, even though
; they might mean something in the future.

; Directives following the section heading [PATH=/www/mysite] only
; apply to PHP files in the /www/mysite directory.  Directives
; following the section heading [HOST=www.example.com] only apply to
; PHP files served from www.example.com.  Directives set in these
; special sections cannot be overridden by user-defined INI files or
; at runtime. Currently, [PATH=] and [HOST=] sections only work under
; CGI/FastCGI.
; https://php.net/ini.sections

; Directives are specified using the following syntax:
; directive = value
; Directive names are *case sensitive* - foo=bar is different from FOO=bar.
; Directives are variables used to configure PHP or PHP extensions.
; There is no name validation.  If PHP can't find an expected
; directive because it is not set or is mistyped, a default value will be used.

; The value can be a string, a number, a PHP constant (e.g. E_ALL or M_PI), one
; of the INI constants (On, Off, True, False, Yes, No and None) or an expression
; (e.g. E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a
; previously set variable or directive (e.g. ${foo})

; Expressions in the INI file are limited to bitwise operators and parentheses:
; |  bitwise OR
; ^  bitwise XOR
; &  bitwise AND
; ~  bitwise NOT
; !  boolean NOT

; Boolean flags can be turned on using the values 1, On, True or Yes.
; They can be turned off using the values 0, Off, False or No.

; An empty string can be denoted by simply not writing anything after the equal
; sign, or by using the None keyword:

; foo =         ; sets foo to an empty string
; foo = None    ; sets foo to an empty string
; foo = "None"  ; sets foo to the string 'None'

; If you use constants in your value, and these constants belong to a
; dynamically loaded extension (either a PHP extension or a Zend extension),
; you may only use these constants *after* the line that loads the extension.

;;;;;;;;;;;;;;;;;;;
; About this file ;
;;;;;;;;;;;;;;;;;;;
; PHP comes packaged with two INI files. One that is recommended to be used
; in production environments and one that is recommended to be used in
; development environments.

; php.ini-production contains settings which hold security, performance and
; best practices at its core. But please be aware, these settings may break
; compatibility with older or less security-conscious applications. We
; recommending using the production ini in production and testing environments.

; php.ini-development is very similar to its production variant, except it is
; much more verbose when it comes to errors. We recommend using the
; development version only in development environments, as errors shown to
; application users can inadvertently leak otherwise secure information.

; This is the php.ini-production INI file.

;;;;;;;;;;;;;;;;;;;
; Quick Reference ;
;;;;;;;;;;;;;;;;;;;

; The following are all the settings which are different in either the production
; or development versions of the INIs with respect to PHP's default behavior.
; Please see the actual settings later in the document for more details as to why
; we recommend these changes in PHP's behavior.

; display_errors
;   Default Value: On
;   Development Value: On
;   Production Value: Off

; display_startup_errors
;   Default Value: On
;   Development Value: On
;   Production Value: Off

; error_reporting
;   Default Value: E_ALL
;   Development Value: E_ALL
;   Production Value: E_ALL & ~E_DEPRECATED

; log_errors
;   Default Value: Off
;   Development Value: On
;   Production Value: On

; max_input_time
;   Default Value: -1 (Unlimited)
;   Development Value: 60 (60 seconds)
;   Production Value: 60 (60 seconds)

; output_buffering
;   Default Value: Off
;   Development Value: 4096
;   Production Value: 4096

; register_argc_argv
;   Default Value: On
;   Development Value: Off
;   Production Value: Off

; request_order
;   Default Value: None
;   Development Value: "GP"
;   Production Value: "GP"

; session.gc_divisor
;   Default Value: 100
;   Development Value: 1000
;   Production Value: 1000

; short_open_tag
;   Default Value: On
;   Development Value: Off
;   Production Value: Off

; variables_order
;   Default Value: "EGPCS"
;   Development Value: "GPCS"
;   Production Value: "GPCS"

; zend.assertions
;   Default Value: 1
;   Development Value: 1
;   Production Value: -1

; zend.exception_ignore_args
;   Default Value: Off
;   Development Value: Off
;   Production Value: On

; zend.exception_string_param_max_len
;   Default Value: 15
;   Development Value: 15
;   Production Value: 0

;;;;;;;;;;;;;;;;;;;;
; php.ini Options  ;
;;;;;;;;;;;;;;;;;;;;
; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini"
;user_ini.filename = ".user.ini"

; To disable this feature set this option to an empty value
;user_ini.filename =

; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes)
;user_ini.cache_ttl = 300

;;;;;;;;;;;;;;;;;;;;
; Language Options ;
;;;;;;;;;;;;;;;;;;;;

; Enable the PHP scripting language engine under Apache.
; https://php.net/engine
engine = On

; This directive determines whether or not PHP will recognize code between
; <? and ?> tags as PHP source which should be processed as such. It is
; generally recommended that <?php and ?> should be used and that this feature
; should be disabled, as enabling it may result in issues when generating XML
; documents, however this remains supported for backward compatibility reasons.
; Note that this directive does not control the <?= shorthand tag, which can be
; used regardless of this directive.
; Default Value: On
; Development Value: Off
; Production Value: Off
; https://php.net/short-open-tag
short_open_tag = Off

; The number of significant digits displayed in floating point numbers.
; https://php.net/precision
precision = 14

; Output buffering is a mechanism for controlling how much output data
; (excluding headers and cookies) PHP should keep internally before pushing that
; data to the client. If your application's output exceeds this setting, PHP
; will send that data in chunks of roughly the size you specify.
; Turning on this setting and managing its maximum buffer size can yield some
; interesting side-effects depending on your application and web server.
; You may be able to send headers and cookies after you've already sent output
; through print or echo. You also may see performance benefits if your server is
; emitting less packets due to buffered output versus PHP streaming the output
; as it gets it. On production servers, 4096 bytes is a good setting for performance
; reasons.
; Note: Output buffering can also be controlled via Output Buffering Control
;   functions.
; Possible Values:
;   On = Enabled and buffer is unlimited. (Use with caution)
;   Off = Disabled
;   Integer = Enables the buffer and sets its maximum size in bytes.
; Note: This directive is hardcoded to Off for the CLI SAPI
; Default Value: Off
; Development Value: 4096
; Production Value: 4096
; https://php.net/output-buffering
output_buffering = 4096


[PHP]

;;;;;;;;;;;;;;;;;;;
; About php.ini   ;
;;;;;;;;;;;;;;;;;;;
; PHP's initialization file, generally called php.ini, is responsible for
; configuring many of the aspects of PHP's behavior.

; PHP attempts to find and load this configuration from a number of locations.
; The following is a summary of its search order:
; 1. SAPI module specific location.
; 2. The PHPRC environment variable.
; 3. A number of predefined registry keys on Windows
; 4. Current working directory (except CLI)
; 5. The web server's directory (for SAPI modules), or directory of PHP
; (otherwise in Windows)
; 6. The directory from the --with-config-file-path compile time option, or the
; Windows directory (usually C:\windows)
; See the PHP docs for more specific information.
; https://php.net/configuration.file

; The syntax of the file is extremely simple.  Whitespace and lines
; beginning with a semicolon are silently ignored (as you probably guessed).
; Section headers (e.g. [Foo]) are also silently ignored, even though
; they might mean something in the future.

; Directives following the section heading [PATH=/www/mysite] only
; apply to PHP files in the /www/mysite directory.  Directives
; following the section heading [HOST=www.example.com] only apply to
; PHP files served from www.example.com.  Directives set in these
; special sections cannot be overridden by user-defined INI files or
; at runtime. Currently, [PATH=] and [HOST=] sections only work under
; CGI/FastCGI.
; https://php.net/ini.sections

; Directives are specified using the following syntax:
; directive = value
; Directive names are *case sensitive* - foo=bar is different from FOO=bar.
; Directives are variables used to configure PHP or PHP extensions.
; There is no name validation.  If PHP can't find an expected
; directive because it is not set or is mistyped, a default value will be used.

; The value can be a string, a number, a PHP constant (e.g. E_ALL or M_PI), one
; of the INI constants (On, Off, True, False, Yes, No and None) or an expression
; (e.g. E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a
; previously set variable or directive (e.g. ${foo})

; Expressions in the INI file are limited to bitwise operators and parentheses:
; |  bitwise OR
; ^  bitwise XOR
; &  bitwise AND
; ~  bitwise NOT
; !  boolean NOT

; Boolean flags can be turned on using the values 1, On, True or Yes.
; They can be turned off using the values 0, Off, False or No.

; An empty string can be denoted by simply not writing anything after the equal
; sign, or by using the None keyword:

; foo =         ; sets foo to an empty string
; foo = None    ; sets foo to an empty string
; foo = "None"  ; sets foo to the string 'None'

; If you use constants in your value, and these constants belong to a
; dynamically loaded extension (either a PHP extension or a Zend extension),
; you may only use these constants *after* the line that loads the extension.

;;;;;;;;;;;;;;;;;;;
; About this file ;
;;;;;;;;;;;;;;;;;;;
; PHP comes packaged with two INI files. One that is recommended to be used
; in production environments and one that is recommended to be used in
; development environments.

; php.ini-production contains settings which hold security, performance and
; best practices at its core. But please be aware, these settings may break
; compatibility with older or less security-conscious applications. We
; recommending using the production ini in production and testing environments.

; php.ini-development is very similar to its production variant, except it is
; much more verbose when it comes to errors. We recommend using the
; development version only in development environments, as errors shown to
; application users can inadvertently leak otherwise secure information.

; This is the php.ini-development INI file.

;;;;;;;;;;;;;;;;;;;
; Quick Reference ;
;;;;;;;;;;;;;;;;;;;

; The following are all the settings which are different in either the production
; or development versions of the INIs with respect to PHP's default behavior.
; Please see the actual settings later in the document for more details as to why
; we recommend these changes in PHP's behavior.

; display_errors
;   Default Value: On
;   Development Value: On
;   Production Value: Off

; display_startup_errors
;   Default Value: On
;   Development Value: On
;   Production Value: Off

; error_reporting
;   Default Value: E_ALL
;   Development Value: E_ALL
;   Production Value: E_ALL & ~E_DEPRECATED

; log_errors
;   Default Value: Off
;   Development Value: On
;   Production Value: On

; max_input_time
;   Default Value: -1 (Unlimited)
;   Development Value: 60 (60 seconds)
;   Production Value: 60 (60 seconds)

; output_buffering
;   Default Value: Off
;   Development Value: 4096
;   Production Value: 4096

; register_argc_argv
;   Default Value: On
;   Development Value: Off
;   Production Value: Off

; request_order
;   Default Value: None
;   Development Value: "GP"
;   Production Value: "GP"

; session.gc_divisor
;   Default Value: 100
;   Development Value: 1000
;   Production Value: 1000

; short_open_tag
;   Default Value: On
;   Development Value: Off
;   Production Value: Off

; variables_order
;   Default Value: "EGPCS"
;   Development Value: "GPCS"
;   Production Value: "GPCS"

; zend.assertions
;   Default Value: 1
;   Development Value: 1
;   Production Value: -1

; zend.exception_ignore_args
;   Default Value: Off
;   Development Value: Off
;   Production Value: On

; zend.exception_string_param_max_len
;   Default Value: 15
;   Development Value: 15
;   Production Value: 0

;;;;;;;;;;;;;;;;;;;;
; php.ini Options  ;
;;;;;;;;;;;;;;;;;;;;
; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini"
;user_ini.filename = ".user.ini"

; To disable this feature set this option to an empty value
;user_ini.filename =

; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes)
;user_ini.cache_ttl = 300

;;;;;;;;;;;;;;;;;;;;
; Language Options ;
;;;;;;;;;;;;;;;;;;;;

; Enable the PHP scripting language engine under Apache.
; https://php.net/engine
engine = On

; This directive determines whether or not PHP will recognize code between
; <? and ?> tags as PHP source which should be processed as such. It is
; generally recommended that <?php and ?> should be used and that this feature
; should be disabled, as enabling it may result in issues when generating XML
; documents, however this remains supported for backward compatibility reasons.
; Note that this directive does not control the <?= shorthand tag, which can be
; used regardless of this directive.
; Default Value: On
; Development Value: Off
; Production Value: Off
; https://php.net/short-open-tag
short_open_tag = Off

; The number of significant digits displayed in floating point numbers.
; https://php.net/precision
precision = 14

; Output buffering is a mechanism for controlling how much output data
; (excluding headers and cookies) PHP should keep internally before pushing that
; data to the client. If your application's output exceeds this setting, PHP
; will send that data in chunks of roughly the size you specify.
; Turning on this setting and managing its maximum buffer size can yield some
; interesting side-effects depending on your application and web server.
; You may be able to send headers and cookies after you've already sent output
; through print or echo. You also may see performance benefits if your server is
; emitting less packets due to buffered output versus PHP streaming the output
; as it gets it. On production servers, 4096 bytes is a good setting for performance
; reasons.
; Note: Output buffering can also be controlled via Output Buffering Control
;   functions.
; Possible Values:
;   On = Enabled and buffer is unlimited. (Use with caution)
;   Off = Disabled
;   Integer = Enables the buffer and sets its maximum size in bytes.
; Note: This directive is hardcoded to Off for the CLI SAPI
; Default Value: Off
; Development Value: 4096
; Production Value: 4096
; https://php.net/output-buffering
output_buffering = 4096


#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1
Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.
#endif

typedef union { double d; ULong L[2]; } U;

#ifdef IEEE_8087
#define word0(x) (x)->L[1]
#define word1(x) (x)->L[0]
#else
#define word0(x) (x)->L[0]
#define word1(x) (x)->L[1]
#endif
#define dval(x) (x)->d

#ifndef STRTOD_DIGLIM
#define STRTOD_DIGLIM 40
#endif

#ifdef DIGLIM_DEBUG
extern int strtod_diglim;
#else
#define strtod_diglim STRTOD_DIGLIM
#endif

/* The following definition of Storeinc is appropriate for MIPS processors.
 * An alternative that might be better on some machines is
 * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
 */
#if defined(IEEE_8087) + defined(VAX) + defined(__arm__)
#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \
((unsigned short *)a)[0] = (unsigned short)c, a++)
#else
#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \
((unsigned short *)a)[1] = (unsigned short)c, a++)
#endif

/* #define P DBL_MANT_DIG */
/* Ten_pmax = floor(P*log(2)/log(5)) */
/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */

#ifdef IEEE_Arith
#define Exp_shift  20
#define Exp_shift1 20
#define Exp_msk1    0x100000
#define Exp_msk11   0x100000
#define Exp_mask  0x7ff00000
#define P 53
#define Nbits 53
#define Bias 1023
#define Emax 1023
#define Emin (-1022)
#define Exp_1  0x3ff00000
#define Exp_11 0x3ff00000
#define Ebits 11
#define Frac_mask  0xfffff
#define Frac_mask1 0xfffff
#define Ten_pmax 22
#define Bletch 0x10
#define Bndry_mask  0xfffff
#define Bndry_mask1 0xfffff
#define LSB 1
#define Sign_bit 0x80000000
#define Log2P 1
#define Tiny0 0
#define Tiny1 1
#define Quick_max 14
#define Int_max 14
#ifndef NO_IEEE_Scale
#define Avoid_Underflow
#ifdef Flush_Denorm	/* debugging option */
#undef Sudden_Underflow
#endif
#endif

#ifndef Flt_Rounds
#ifdef FLT_ROUNDS
#define Flt_Rounds FLT_ROUNDS
#else
#define Flt_Rounds 1
#endif
#endif /*Flt_Rounds*/

#ifdef Honor_FLT_ROUNDS
#undef Check_FLT_ROUNDS
#define Check_FLT_ROUNDS
#else
#define Rounding Flt_Rounds
#endif

#else /* ifndef IEEE_Arith */
#undef Check_FLT_ROUNDS
#undef Honor_FLT_ROUNDS
#undef SET_INEXACT
#undef  Sudden_Underflow
#define Sudden_Underflow
#ifdef IBM
#undef Flt_Rounds
#define Flt_Rounds 0
#define Exp_shift  24
#define Exp_shift1 24
#define Exp_msk1   0x1000000
#define Exp_msk11  0x1000000
#define Exp_mask  0x7f000000
#define P 14
#define Nbits 56
#define Bias 65
#define Emax 248
#define Emin (-260)
#define Exp_1  0x41000000
#define Exp_11 0x41000000
#define Ebits 8	/* exponent has 7 bits, but 8 is the right value in b2d */
#define Frac_mask  0xffffff
#define Frac_mask1 0xffffff
#define Bletch 4
#define Ten_pmax 22
#define Bndry_mask  0xefffff
#define Bndry_mask1 0xffffff
#define LSB 1
#define Sign_bit 0x80000000
#define Log2P 4
#define Tiny0 0x100000
#define Tiny1 0
#define Quick_max 14
#define Int_max 15
#else /* VAX */
#undef Flt_Rounds
#define Flt_Rounds 1
#define Exp_shift  23
#define Exp_shift1 7
#define Exp_msk1    0x80
#define Exp_msk11   0x800000
#define Exp_mask  0x7f80
#define P 56
#define Nbits 56
#define Bias 129
#define Emax 126
#define Emin (-129)
#define Exp_1  0x40800000
#define Exp_11 0x4080
#define Ebits 8
#define Frac_mask  0x7fffff
#define Frac_mask1 0xffff007f
#define Ten_pmax 24
#define Bletch 2
#define Bndry_mask  0xffff007f
#define Bndry_mask1 0xffff007f
#define LSB 0x10000
#define Sign_bit 0x8000
#define Log2P 1
#define Tiny0 0x80
#define Tiny1 0
#define Quick_max 15
#define Int_max 15
#endif /* IBM, VAX */
#endif /* IEEE_Arith */

#ifndef IEEE_Arith
#define ROUND_BIASED
#else
#ifdef ROUND_BIASED_without_Round_Up
#undef  ROUND_BIASED
#define ROUND_BIASED
#endif
#endif

#ifdef RND_PRODQUOT
#define rounded_product(a,b) a = rnd_prod(a, b)
#define rounded_quotient(a,b) a = rnd_quot(a, b)
#ifdef KR_headers
extern double rnd_prod(), rnd_quot();
#else
extern double rnd_prod(double, double), rnd_quot(double, double);
#endif
#else
#define rounded_product(a,b) a *= b
#define rounded_quotient(a,b) a /= b
#endif

#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
#define Big1 0xffffffff

#ifndef Pack_32
#define Pack_32
#endif

typedef struct BCinfo BCinfo;
 struct
BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; };

#ifdef KR_headers
#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff)
#else
#define FFFFFFFF 0xffffffffUL
#endif

#ifdef NO_LONG_LONG
#undef ULLong
#ifdef Just_16
#undef Pack_32
/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.
 * This makes some inner loops simpler and sometimes saves work
 * during multiplications, but it often seems to make things slightly
 * slower.  Hence the default is now to store 32 bits per Long.
 */
#endif
#else	/* long long available */
#ifndef Llong
#define Llong long long
#endif
#ifndef ULLong
#define ULLong unsigned Llong
#endif
#endif /* NO_LONG_LONG */

#ifndef MULTIPLE_THREADS
#define ACQUIRE_DTOA_LOCK(n)	/*nothing*/
#define FREE_DTOA_LOCK(n)	/*nothing*/
#endif

#define Kmax ZEND_STRTOD_K_MAX

 struct
Bigint {
	struct Bigint *next;
	int k, maxwds, sign, wds;
	ULong x[1];
	};

 typedef struct Bigint Bigint;

#ifndef Bigint
 static Bigint *freelist[Kmax+1];
#endif

static void destroy_freelist(void);
static void free_p5s(void);

#ifdef MULTIPLE_THREADS
static MUTEX_T dtoa_mutex;
static MUTEX_T pow5mult_mutex;
#endif /* ZTS */

ZEND_API int zend_shutdown_strtod(void) /* {{{ */
{
	destroy_freelist();
	free_p5s();

	return 1;
}
/* }}} */

 static Bigint *
Balloc
#ifdef KR_headers
	(k) int k;
#else
	(int k)
#endif


--TEST--
Bug #54367 (Use of closure causes problem in ArrayAccess)
--FILE--
<?php
class MyObject implements ArrayAccess
{
    public function offsetSet($offset, $value): void { }
    public function offsetExists($offset): bool {  }
    public function offsetUnset($offset): void { }

    public function offsetGet($offset): mixed
    {
    return function ($var) use ($offset) { // here is the problem
              var_dump($offset, $var);
        };
    }
}

$a = new MyObject();
echo $a['p']('foo');
?>
--EXPECT--
string(1) "p"
string(3) "foo"


--TEST--
Bug #43483 (get_class_methods() does not list all visible methods)
--FILE--
<?php
class C {
    public static function test() {
        D::prot();
        print_r(get_class_methods("D"));
    }
}
class D extends C {
    protected static function prot() {
        echo "Successfully called D::prot().\n";
    }
}
D::test();
?>
--EXPECT--
Successfully called D::prot().
Array
(
    [0] => prot
    [1] => test
)


--TEST--
File with just a <?php tag should be valid
--FILE_EXTERNAL--
php_tag_only.inc
--EXPECT--


--TEST--
Using traits to implement interface
--FILE--
<?php

trait foo {
    public function abc() {
    }
}

interface baz {
    public function abc();
}

class bar implements baz {
    use foo;

}

new bar;
print "OK\n";

?>
--EXPECT--
OK


--TEST--
Bug #55137 (Changing trait static method visibility)
--FILE--
<?php

trait A {
   protected static function foo() { echo "abc\n"; }
   private static function bar() { echo "def\n"; }
}

class B {
   use A {
      A::foo as public;
      A::bar as public baz;
   }
}

B::foo();
B::baz();

?>
--EXPECT--
abc
def


--TEST--
Bug #76773 (Traits used on the parent are ignored for child classes)
--FILE--
<?php

trait MyTrait
{
    public function hello()
    {
        echo __CLASS__, "\n";

        if (get_parent_class(__CLASS__) !== false) {
            parent::hello();
        }
    }
}

class ParentClass
{
    use MyTrait;
}

class ChildClass extends ParentClass
{
    use MyTrait;
}

$c = new ChildClass();
$c->hello();
?>
--EXPECT--
ChildClass
ParentClass


--TEST--
Checking error message when the trait doesn't implements the interface
--FILE--
<?php

trait foo {
    public function a() {
    }
}

interface baz {
    public function abc();
}

class bar implements baz {
    use foo;

}

new bar;

?>
--EXPECTF--
Fatal error: Class bar contains 1 abstract method and must therefore be declared abstract or implement the remaining method (baz::abc) in %s on line %d


--TEST--
Abstract Trait Methods should behave like common abstract methods and
implementation may be provided by other traits. Sorting order shouldn't influence result.
--FILE--
<?php
error_reporting(E_ALL);

trait THello {
  public abstract function hello();
}

trait THelloImpl {
  public function hello() {
    echo 'Hello';
  }
}

class TraitsTest1 {
    use THello;
    use THelloImpl;
}

$test = new TraitsTest1();
$test->hello();

class TraitsTest2 {
    use THelloImpl;
    use THello;
}

$test = new TraitsTest2();
$test->hello();

?>
--EXPECT--
HelloHello


--TEST--
002: Import - different syntaxes
--FILE--
<?php
namespace test\ns1;

class Foo {
  static function bar() {
    echo __CLASS__,"\n";
  }
}

class Foo2 {
  static function bar() {
    echo __CLASS__,"\n";
  }
}

namespace xyz;
use test\ns1\Foo;
use test\ns1\Foo as Bar;
use \test\ns1\Foo2;
use \test\ns1\Foo2 as Bar2;

Foo::bar();
Bar::bar();
Foo2::bar();
Bar2::bar();
?>
--EXPECT--
test\ns1\Foo
test\ns1\Foo
test\ns1\Foo2
test\ns1\Foo2


--TEST--
065: Multiple names in use statement
--FILE--
<?php
use X\Y as test, X\Z as test2;

require "ns_065.inc";

test\foo();
test2\foo();
?>
--EXPECT--
X\Y\foo
X\Z\foo


--TEST--
#[\Override]: On used trait with interface method.
--FILE--
<?php

trait T {
    #[\Override]
    public function i(): void {}
}

interface I {
    public function i(): void;
}

class Foo implements I {
    use T;
}

echo "Done";

?>
--EXPECT--
Done


--TEST--
#[\Override]: Redeclared trait method with interface.
--FILE--
<?php

interface I {
    public function i(): string;
}

trait T {
    public function i(): string {
        return 'T';
    }
}

class C implements I {
    use T;

    #[\Override]
    public function i(): string {
        return 'C';
    }
}

var_dump((new C())->i());

echo "Done";

?>
--EXPECT--
string(1) "C"
Done


--TEST--
#[Override] attribute in trait does not check for parent class implementations
--FILE--
<?php

class A {
    public function foo(): void {}
}

interface I {
    public function foo(): void;
}

trait T {
    #[\Override]
    public function foo(): void {
        echo 'foo';
    }
}

// Works fine
class B implements I {
    use T;
}

// Works fine ("copied and pasted into the target class")
class C extends A {
    #[\Override]
    public function foo(): void {
        echo 'foo';
    }
}

// Does not work
class D extends A {
    use T;
}
echo "Done";

?>
--EXPECT--
Done


--TEST--
#[Override] attribute in trait does not check for parent class implementations (Variant with abstract __construct)
--FILE--
<?php

abstract class A {
    abstract public function __construct();
}

trait T {
    #[\Override]
    public function __construct() {
        echo 'foo';
    }
}

class D extends A {
    use T;
}
echo "Done";

?>
--EXPECT--
Done


--TEST--
#[Override] attribute in trait does not check for parent class implementations (Variant with protected parent method)
--FILE--
<?php

class A {
    protected function foo(): void {}
}

trait T {
    #[\Override]
    public function foo(): void {
        echo 'foo';
    }
}

class D extends A {
    use T;
}
echo "Done";

?>
--EXPECTF--
Done


--TEST--
Closure 002: Lambda with lexical variables (global scope)
--FILE--
<?php

$x = 4;

$lambda1 = function () use ($x) {
    echo "$x\n";
};

$lambda2 = function () use (&$x) {
    echo "$x\n";
};

$lambda1();
$lambda2();
$x++;
$lambda1();
$lambda2();

echo "Done\n";
?>
--EXPECT--
4
4
4
5
Done


--TEST--
Closure 012: Undefined lexical variables
--FILE--
<?php
$lambda = function () use ($i) {
    return ++$i;
};
$lambda();
$lambda();
var_dump($i);
$lambda = function () use (&$i) {
    return ++$i;
};
$lambda();
$lambda();
var_dump($i);
?>
--EXPECTF--
Warning: Undefined variable $i in %s on line %d

Warning: Undefined variable $i in %s on line %d
NULL
int(2)


--TEST--
aliasing imported functions to resolve naming conflicts
--FILE--
<?php

namespace foo {
    function baz() {
        return 'foo.baz';
    }
}

namespace bar {
    function baz() {
        return 'bar.baz';
    }
}

namespace {
    use function foo\baz as foo_baz,
                 bar\baz as bar_baz;
    var_dump(foo_baz());
    var_dump(bar_baz());
    echo "Done\n";
}

?>
--EXPECT--
string(7) "foo.baz"
string(7) "bar.baz"
Done


--TEST--
Inline HTML should not be split at partial PHP tags
--EXTENSIONS--
tokenizer
--INI--
short_open_tag=0
--FILE--
<?php

var_dump(token_get_all(<<<'PHP'
Foo<?phpBar
PHP));

?>
--EXPECTF--
array(1) {
  [0]=>
  array(3) {
    [0]=>
    int(%d)
    [1]=>
    string(11) "Foo<?phpBar"
    [2]=>
    int(1)
  }
}


--TEST--
Test is_array() function
--FILE--
<?php
echo "*** Testing is_array() on different type of arrays ***\n";
/* different types of arrays */
$arrays = array(
  array(),
  array(NULL),
  array(null),
  array(true),
  array(""),
  array(''),
  array(array(), array()),
  array(array(1, 2), array('a', 'b')),
  array(1 => 'One'),
  array("test" => "is_array"),
  array(0),
  array(-1),
  array(10.5, 5.6),
  array("string", "test"),
  array('string', 'test')
);
/* loop to check that is_array() recognizes different
   type of arrays, expected output bool(true) */
$loop_counter = 1;
foreach ($arrays as $var_array ) {
  echo "-- Iteration $loop_counter --\n"; $loop_counter++;
  var_dump( is_array ($var_array) );
}

echo "\n*** Testing is_array() on non array types ***\n";

// get a resource type variable
$fp = fopen (__FILE__, "r");
$dfp = opendir ( __DIR__ );

// unset variables
$unset_array = array(10);
unset($unset_array);

// other types in a array
$varient_arrays = array (
  /* integers */
  543915,
  -5322,
  0x55F,
  -0xCCF,
  123,
  -0654,

  /* strings */
  "",
  '',
  "0",
  '0',
  'string',
  "string",

  /* floats */
  10.0000000000000000005,
  .5e6,
  -.5E7,
  .5E+8,
  -.5e+90,
  1e5,

  /* objects */
  new stdclass,

  /* resources */
  $fp,
  $dfp,

  /* nulls */
  null,
  NULL,

  /* boolean */
  true,
  TRUE,
  FALSE,
  false,

  /* unset/undefined arrays  */
  @$unset_array,
  @$undefined_array
);
/* loop through the $varient_array to see working of
   is_array() on non array types, expected output bool(false) */
$loop_counter = 1;
foreach ($varient_arrays as $type ) {
  echo "-- Iteration $loop_counter --\n"; $loop_counter++;
  var_dump( is_array ($type) );
}

echo "Done\n";
/* close resources */
fclose($fp);
closedir($dfp);
?>
--EXPECT--
*** Testing is_array() on different type of arrays ***
-- Iteration 1 --
bool(true)
-- Iteration 2 --
bool(true)
-- Iteration 3 --
bool(true)
-- Iteration 4 --
bool(true)
-- Iteration 5 --
bool(true)
-- Iteration 6 --
bool(true)
-- Iteration 7 --
bool(true)
-- Iteration 8 --
bool(true)
-- Iteration 9 --
bool(true)
-- Iteration 10 --
bool(true)
-- Iteration 11 --
bool(true)
-- Iteration 12 --
bool(true)
-- Iteration 13 --
bool(true)
-- Iteration 14 --
bool(true)
-- Iteration 15 --
bool(true)

*** Testing is_array() on non array types ***
-- Iteration 1 --
bool(false)
-- Iteration 2 --
bool(false)
-- Iteration 3 --
bool(false)
-- Iteration 4 --
bool(false)
-- Iteration 5 --
bool(false)
-- Iteration 6 --
bool(false)
-- Iteration 7 --
bool(false)
-- Iteration 8 --
bool(false)
-- Iteration 9 --
bool(false)
-- Iteration 10 --
bool(false)
-- Iteration 11 --
bool(false)
-- Iteration 12 --
bool(false)
-- Iteration 13 --
bool(false)
-- Iteration 14 --
bool(false)
-- Iteration 15 --
bool(false)
-- Iteration 16 --
bool(false)
-- Iteration 17 --
bool(false)
-- Iteration 18 --
bool(false)
-- Iteration 19 --
bool(false)
-- Iteration 20 --
bool(false)
-- Iteration 21 --
bool(false)
-- Iteration 22 --
bool(false)
-- Iteration 23 --
bool(false)
-- Iteration 24 --
bool(false)
-- Iteration 25 --
bool(false)
-- Iteration 26 --
bool(false)
-- Iteration 27 --
bool(false)
-- Iteration 28 --
bool(false)
-- Iteration 29 --
bool(false)
Done


--TEST--
Test array_is_list() function
--FILE--
<?php

function test_is_list(string $desc, $val) : void {
    try {
        printf("%s: %s\n", $desc, json_encode(array_is_list($val)));
    } catch (TypeError $e) {
        printf("%s: threw %s\n", $desc, $e->getMessage());
    }
}

test_is_list("empty", []);
test_is_list("one", [1]);
test_is_list("two", [1,2]);
test_is_list("three", [1,2,3]);
test_is_list("four", [1,2,3,4]);
test_is_list("ten", range(0, 10));

test_is_list("null", null);
test_is_list("int", 123);
test_is_list("float", 1.23);
test_is_list("string", "string");
test_is_list("object", new stdClass());
test_is_list("true", true);
test_is_list("false", false);

test_is_list("string key", ["a" => 1]);
test_is_list("mixed keys", [0 => 0, "a" => 1]);
test_is_list("ordered keys", [0 => 0, 1 => 1]);
test_is_list("shuffled keys", [1 => 0, 0 => 1]);
test_is_list("skipped keys", [0 => 0, 2 => 2]);

$arr = [1, 2, 3];
unset($arr[0]);
test_is_list("unset first", $arr);

$arr = [1, 2, 3];
unset($arr[1]);
test_is_list("unset middle", $arr);

$arr = [1, 2, 3];
unset($arr[2]);
test_is_list("unset end", $arr);

$arr = [1, "a" => "a", 2];
unset($arr["a"]);
test_is_list("unset string key", $arr);

$arr = [1 => 1, 0 => 0];
unset($arr[1]);
test_is_list("unset into order", $arr);

$arr = ["a" => 1];
unset($arr["a"]);
test_is_list("unset to empty", $arr);

$arr = [1, 2, 3];
$arr[] = 4;
test_is_list("append implicit", $arr);

$arr = [1, 2, 3];
$arr[3] = 4;
test_is_list("append explicit", $arr);

$arr = [1, 2, 3];
$arr[4] = 5;
test_is_list("append with gap", $arr);

?>
--EXPECT--
empty: true
one: true
two: true
three: true
four: true
ten: true
null: threw array_is_list(): Argument #1 ($array) must be of type array, null given
int: threw array_is_list(): Argument #1 ($array) must be of type array, int given
float: threw array_is_list(): Argument #1 ($array) must be of type array, float given
string: threw array_is_list(): Argument #1 ($array) must be of type array, string given
object: threw array_is_list(): Argument #1 ($array) must be of type array, stdClass given
true: threw array_is_list(): Argument #1 ($array) must be of type array, true given
false: threw array_is_list(): Argument #1 ($array) must be of type array, false given
string key: false
mixed keys: false
ordered keys: true
shuffled keys: false
skipped keys: false
unset first: false
unset middle: false
unset end: true
unset string key: true
unset into order: true
unset to empty: true
append implicit: true
append explicit: true
append with gap: false


--TEST--
Test array_keys() function (variation - 3)
--FILE--
<?php

echo "*** Testing array_keys() on all the types other than arrays ***\n";
$types_arr = array(
  TRUE => TRUE,
  FALSE => FALSE,
  1 => 1,
  0 => 0,
  -1 => -1,
  "1" => "1",
  "0" => "0",
  "-1" => "-1",
  NULL,
  array(),
  "php" => "php",
  "" => ""
);
$values = array(TRUE, FALSE, 1, 0, -1, "1", "0", "-1",  NULL, array(), "php", "");
foreach ($values as $value){
    var_dump($value);
    var_dump(array_keys($types_arr, $value));
}

echo "Done\n";
?>
--EXPECT--
*** Testing array_keys() on all the types other than arrays ***
bool(true)
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(-1)
  [2]=>
  string(3) "php"
}
bool(false)
array(4) {
  [0]=>
  int(0)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  string(0) ""
}
int(1)
array(1) {
  [0]=>
  int(1)
}
int(0)
array(2) {
  [0]=>
  int(0)
  [1]=>
  int(2)
}
int(-1)
array(1) {
  [0]=>
  int(-1)
}
string(1) "1"
array(1) {
  [0]=>
  int(1)
}
string(1) "0"
array(1) {
  [0]=>
  int(0)
}
string(2) "-1"
array(1) {
  [0]=>
  int(-1)
}
NULL
array(3) {
  [0]=>
  int(2)
  [1]=>
  int(3)
  [2]=>
  string(0) ""
}
array(0) {
}
array(2) {
  [0]=>
  int(2)
  [1]=>
  int(3)
}
string(3) "php"
array(1) {
  [0]=>
  string(3) "php"
}
string(0) ""
array(2) {
  [0]=>
  int(2)
  [1]=>
  string(0) ""
}
Done


--TEST--
Test array_filter() function : usage variations - Different types of array for 'input' argument
--FILE--
<?php
/*
* Passing different types of array as 'input' argument.
*/

function always_false($input)
{
  return false;
}

// callback function returning always true
function always_true($input)
{
  return true;
}

echo "*** Testing array_filter() : usage variations - different types of array for 'input' argument***\n";

// different types of 'input' array
$input_values = array(
  array(0, 1, 2, -1, 034, 0X4A),  // integer values
  array(0.0, 1.2, 1.2e3, 1.2e-3),  // float values
  array('value1', "value2", '', " ", ""),  // string values
  array(true, false, TRUE, FALSE),  // bool values
  array(null, NULL),  // null values
  array(1 => 'one', 'zero' => 0, -2 => "value"), //associative array
  array("one" => 1, null => 'null', 5 => "float", true => 1, "" => 'empty'),  // associative array with different keys
  array(1 => 'one', 2, "key" => 'value')  // combinition of associative and non-associative array

);

// loop through each element of 'input' with default callback
for($count = 0; $count < count($input_values); $count++)
{
  echo "-- Iteration ".($count + 1). " --\n";
  var_dump( array_filter($input_values[$count]) );
  var_dump( array_filter($input_values[$count], 'always_true') );
  var_dump( array_filter($input_values[$count], 'always_false') );
}

echo "Done"
?>
--EXPECT--
*** Testing array_filter() : usage variations - different types of array for 'input' argument***
-- Iteration 1 --
array(5) {
  [1]=>
  int(1)
  [2]=>
  int(2)
  [3]=>
  int(-1)
  [4]=>
  int(28)
  [5]=>
  int(74)
}
array(6) {
  [0]=>
  int(0)
  [1]=>
  int(1)
  [2]=>
  int(2)
  [3]=>
  int(-1)
  [4]=>
  int(28)
  [5]=>
  int(74)
}
array(0) {
}
-- Iteration 2 --
array(3) {
  [1]=>
  float(1.2)
  [2]=>
  float(1200)
  [3]=>
  float(0.0012)
}
array(4) {
  [0]=>
  float(0)
  [1]=>
  float(1.2)
  [2]=>
  float(1200)
  [3]=>
  float(0.0012)
}
array(0) {
}
-- Iteration 3 --
array(3) {
  [0]=>
  string(6) "value1"
  [1]=>
  string(6) "value2"
  [3]=>
  string(1) " "
}
array(5) {
  [0]=>
  string(6) "value1"
  [1]=>
  string(6) "value2"
  [2]=>
  string(0) ""
  [3]=>
  string(1) " "
  [4]=>
  string(0) ""
}
array(0) {
}
-- Iteration 4 --
array(2) {
  [0]=>
  bool(true)
  [2]=>
  bool(true)
}
array(4) {
  [0]=>
  bool(true)
  [1]=>
  bool(false)
  [2]=>
  bool(true)
  [3]=>
  bool(false)
}
array(0) {
}
-- Iteration 5 --
array(0) {
}
array(2) {
  [0]=>
  NULL
  [1]=>
  NULL
}
array(0) {
}
-- Iteration 6 --
array(2) {
  [1]=>
  string(3) "one"
  [-2]=>
  string(5) "value"
}
array(3) {
  [1]=>
  string(3) "one"
  ["zero"]=>
  int(0)
  [-2]=>
  string(5) "value"
}
array(0) {
}
-- Iteration 7 --
array(4) {
  ["one"]=>
  int(1)
  [""]=>
  string(5) "empty"
  [5]=>
  string(5) "float"
  [1]=>
  int(1)
}
array(4) {
  ["one"]=>
  int(1)
  [""]=>
  string(5) "empty"
  [5]=>
  string(5) "float"
  [1]=>
  int(1)
}
array(0) {
}
-- Iteration 8 --
array(3) {
  [1]=>
  string(3) "one"
  [2]=>
  int(2)
  ["key"]=>
  string(5) "value"
}
array(3) {
  [1]=>
  string(3) "one"
  [2]=>
  int(2)
  ["key"]=>
  string(5) "value"
}
array(0) {
}
Done


--TEST--
Test array_intersect() function : usage variations - different arrays for 'arr1' argument
--FILE--
<?php
/*
* Passing different types of arrays to $arr1 argument and testing whether
* array_intersect() behaves in expected way with the other arguments passed to the function
* The $arr2 argument is a fixed array.
*/

echo "*** Testing array_intersect() : Passing different types of arrays to \$arr1 argument ***\n";

/* Different heredoc strings passed as argument to $arr1 */
// heredoc with blank line
$blank_line = <<<EOT

EOT;

// heredoc with multiline string
$multiline_string = <<<EOT
hello world
The big brown fox jumped over;
the lazy dog
This is a double quoted string
EOT;

// heredoc with different whitespaces
$diff_whitespaces = <<<EOT
hello\r world\t
1111\t\t != 2222\v\v
heredoc\ndouble quoted string. with\vdifferent\fwhite\vspaces
EOT;

// heredoc with quoted strings and numeric values
$numeric_string = <<<EOT
11 < 12. 123 >22
'single quoted string'
"double quoted string"
2222 != 1111.\t 0000 = 0000\n
EOT;

// arrays to be passed to $arr1 argument
$arrays = array (
/*1*/  array(1, 2), // array with default keys and numeric values
       array(1.1, 2.2), // array with default keys & float values
       array(false,true), // array with default keys and boolean values
       array(), // empty array
/*5*/  array(NULL), // array with NULL
       array("a\v\f","aaaa\r","b","b\tbbb","c","\[\]\!\@\#\$\%\^\&\*\(\)\{\}"),  // array with double quoted strings
       array('a\v\f','aaaa\r','b','b\tbbb','c','\[\]\!\@\#\$\%\^\&\*\(\)\{\}'),  // array with single quoted strings
       array($blank_line, $multiline_string, $diff_whitespaces, $numeric_string),  // array with heredocs

       // associative arrays
/*9*/  array(1 => "one", 2 => "two", 3 => "three"),  // explicit numeric keys, string values
       array("one" => 1, "two" => 2, "three" => 3 ),  // string keys & numeric values
       array( 1 => 10, 2 => 20, 4 => 40, 3 => 30),  // explicit numeric keys and numeric values
       array( "one" => "ten", "two" => "twenty", "three" => "thirty"),  // string key/value
       array("one" => 1, 2 => "two", 4 => "four"),  //mixed

       // associative array, containing null/empty/boolean values as key/value
/*14*/ array(NULL => "NULL", null => "null", "NULL" => NULL, "null" => null),
       array(true => "true", false => "false", "false" => false, "true" => true),
       array("" => "emptyd", '' => 'emptys', "emptyd" => "", 'emptys' => ''),
       array(1 => '', 2 => "", 3 => NULL, 4 => null, 5 => false, 6 => true),
       array('' => 1, "" => 2, NULL => 3, null => 4, false => 5, true => 6),

       // array with repetitive keys
/*19*/ array("One" => 1, "two" => 2, "One" => 10, "two" => 20, "three" => 3)
);


Warning: PDOStatement::execute(): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'some_bool_2' cannot be null in %s
array(3) {
  [0]=>
  string(5) "23000"
  [1]=>
  int(1048)
  [2]=>
  string(35) "Column 'some_bool_2' cannot be null"
}
Array
(
    [uid] => 6
    [0] => 6
    [some_bool_1] => 0
    [1] => 0
    [some_bool_2] => 1
    [2] => 1
    [some_int] => 2
    [3] => 2
)
ok prepare 5
Array
(
    [uid] => 6
    [0] => 6
    [some_bool_1] => 1
    [1] => 1
    [some_bool_2] => 0
    [2] => 0
    [some_int] => 5
    [3] => 5
)


--TEST--
Error during closeCursor() of multi query
--EXTENSIONS--
pdo_mysql
--SKIPIF--
<?php
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
MySQLPDOTest::skip();
?>
--FILE--
<?php
require_once __DIR__ . '/inc/mysql_pdo_test.inc';
$db = MySQLPDOTest::factory();
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);

$stmt = $db->query('SELECT 1; SELECT x FROM does_not_exist');
var_dump($stmt->fetchAll());
var_dump($stmt->closeCursor());
?>
--EXPECTF--
array(1) {
  [0]=>
  array(2) {
    [1]=>
    string(1) "1"
    [0]=>
    string(1) "1"
  }
}

Warning: PDOStatement::closeCursor(): SQLSTATE[42S02]: Base table or view not found: 1146 Table '%s.does_not_exist' doesn't exist in %s on line %d
bool(false)


PHP_FUNCTION(json_decode)
{
	char *str;
	size_t str_len;
	bool assoc = 0; /* return JS objects as PHP objects by default */
	bool assoc_null = 1;
	zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
	zend_long options = 0;

	ZEND_PARSE_PARAMETERS_START(1, 4)
		Z_PARAM_STRING(str, str_len)
		Z_PARAM_OPTIONAL
		Z_PARAM_BOOL_OR_NULL(assoc, assoc_null)
		Z_PARAM_LONG(depth)
		Z_PARAM_LONG(options)
	ZEND_PARSE_PARAMETERS_END();

	if (!(options & PHP_JSON_THROW_ON_ERROR)) {
		JSON_G(error_code) = PHP_JSON_ERROR_NONE;
	}

	if (!str_len) {
		if (!(options & PHP_JSON_THROW_ON_ERROR)) {
			JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
		} else {
			zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX);
		}
		RETURN_NULL();
	}

	if (depth <= 0) {
		zend_argument_value_error(3, "must be greater than 0");
		RETURN_THROWS();
	}

	if (depth > INT_MAX) {
		zend_argument_value_error(3, "must be less than %d", INT_MAX);
		RETURN_THROWS();
	}

	/* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
	if (!assoc_null) {
		if (assoc) {
			options |=  PHP_JSON_OBJECT_AS_ARRAY;
		} else {
			options &= ~PHP_JSON_OBJECT_AS_ARRAY;
		}
	}

	php_json_decode_ex(return_value, str, str_len, options, depth);
}
/* }}} */

/* {{{ Validates if a string contains a valid json */
PHP_FUNCTION(json_validate)
{
	char *str;
	size_t str_len;
	zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
	zend_long options = 0;

	ZEND_PARSE_PARAMETERS_START(1, 3)
		Z_PARAM_STRING(str, str_len)
		Z_PARAM_OPTIONAL
		Z_PARAM_LONG(depth)
		Z_PARAM_LONG(options)
	ZEND_PARSE_PARAMETERS_END();

	if ((options != 0) && (options != PHP_JSON_INVALID_UTF8_IGNORE)) {
		zend_argument_value_error(3, "must be a valid flag (allowed flags: JSON_INVALID_UTF8_IGNORE)");
		RETURN_THROWS();
	}

	if (!str_len) {
		JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
		RETURN_FALSE;
	}

	JSON_G(error_code) = PHP_JSON_ERROR_NONE;

	if (depth <= 0) {
		zend_argument_value_error(2, "must be greater than 0");
		RETURN_THROWS();
	}

	if (depth > INT_MAX) {
		zend_argument_value_error(2, "must be less than %d", INT_MAX);
		RETURN_THROWS();
	}

	RETURN_BOOL(php_json_validate_ex(str, str_len, options, depth));
}
/* }}} */

/* {{{ Returns the error code of the last json_encode() or json_decode() call. */
PHP_FUNCTION(json_last_error)
{
	ZEND_PARSE_PARAMETERS_NONE();

	RETURN_LONG(JSON_G(error_code));
}
/* }}} */

/* {{{ Returns the error string of the last json_encode() or json_decode() call. */
PHP_FUNCTION(json_last_error_msg)
{
	ZEND_PARSE_PARAMETERS_NONE();

	RETURN_STRING(php_json_get_error_msg(JSON_G(error_code)));
}
/* }}} */


--TEST--
JSON (http://www.crockford.com/JSON/JSON_checker/test/pass3.json)
--FILE--
<?php

$test = '
{
    "JSON Test Pattern pass3": {
        "The outermost value": "must be an object or array.",
        "In this test": "It is an object."
    }
}
';

echo 'Testing:' . $test . "\n";
echo "DECODE: AS OBJECT\n";
$obj = json_decode($test);
var_dump($obj);
echo "DECODE: AS ARRAY\n";
$arr = json_decode($test, true);
var_dump($arr);

echo "ENCODE: FROM OBJECT\n";
$obj_enc = json_encode($obj);
echo $obj_enc . "\n";
echo "ENCODE: FROM ARRAY\n";
$arr_enc = json_encode($arr);
echo $arr_enc . "\n";

echo "DECODE AGAIN: AS OBJECT\n";
$obj = json_decode($obj_enc);
var_dump($obj);
echo "DECODE AGAIN: AS ARRAY\n";
$arr = json_decode($arr_enc, true);
var_dump($arr);

?>
--EXPECTF--
Testing:
{
    "JSON Test Pattern pass3": {
        "The outermost value": "must be an object or array.",
        "In this test": "It is an object."
    }
}

DECODE: AS OBJECT
object(stdClass)#%d (1) {
  ["JSON Test Pattern pass3"]=>
  object(stdClass)#%d (2) {
    ["The outermost value"]=>
    string(27) "must be an object or array."
    ["In this test"]=>
    string(16) "It is an object."
  }
}
DECODE: AS ARRAY
array(1) {
  ["JSON Test Pattern pass3"]=>
  array(2) {
    ["The outermost value"]=>
    string(27) "must be an object or array."
    ["In this test"]=>
    string(16) "It is an object."
  }
}
ENCODE: FROM OBJECT
{"JSON Test Pattern pass3":{"The outermost value":"must be an object or array.","In this test":"It is an object."}}
ENCODE: FROM ARRAY
{"JSON Test Pattern pass3":{"The outermost value":"must be an object or array.","In this test":"It is an object."}}
DECODE AGAIN: AS OBJECT
object(stdClass)#%d (1) {
  ["JSON Test Pattern pass3"]=>
  object(stdClass)#%d (2) {
    ["The outermost value"]=>
    string(27) "must be an object or array."
    ["In this test"]=>
    string(16) "It is an object."
  }
}
DECODE AGAIN: AS ARRAY
array(1) {
  ["JSON Test Pattern pass3"]=>
  array(2) {
    ["The outermost value"]=>
    string(27) "must be an object or array."
    ["In this test"]=>
    string(16) "It is an object."
  }
}


--TEST--
JSON (http://www.crockford.com/JSON/JSON_checker/test/pass2.json)
--FILE--
<?php

$test = '[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]';
echo 'Testing: ' . $test . "\n";
echo "DECODE: AS OBJECT\n";
$obj = json_decode($test);
var_dump($obj);
echo "DECODE: AS ARRAY\n";
$arr = json_decode($test, true);
var_dump($arr);

echo "ENCODE: FROM OBJECT\n";
$obj_enc = json_encode($obj);
echo $obj_enc . "\n";
echo "ENCODE: FROM ARRAY\n";
$arr_enc = json_encode($arr);
echo $arr_enc . "\n";

echo "DECODE AGAIN: AS OBJECT\n";
$obj = json_decode($obj_enc);
var_dump($obj);
echo "DECODE AGAIN: AS ARRAY\n";
$arr = json_decode($arr_enc, true);
var_dump($arr);

?>
--EXPECT--
Testing: [[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
DECODE: AS OBJECT
array(1) {
  [0]=>
  array(1) {
    [0]=>
    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          array(1) {
            [0]=>
            array(1) {
              [0]=>
              array(1) {
                [0]=>
                array(1) {
                  [0]=>
                  array(1) {
                    [0]=>
                    array(1) {
                      [0]=>
                      array(1) {
                        [0]=>
                        array(1) {
                          [0]=>
                          array(1) {
                            [0]=>
                            array(1) {
                              [0]=>
                              array(1) {
                                [0]=>
                                array(1) {
                                  [0]=>
                                  array(1) {
                                    [0]=>
                                    array(1) {
                                      [0]=>
                                      string(12) "Not too deep"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
DECODE: AS ARRAY
array(1) {
  [0]=>
  array(1) {
    [0]=>
    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          array(1) {
            [0]=>
            array(1) {
              [0]=>
              array(1) {
                [0]=>
                array(1) {
                  [0]=>
                  array(1) {
                    [0]=>
                    array(1) {
                      [0]=>
                      array(1) {
                        [0]=>
                        array(1) {
                          [0]=>
                          array(1) {
                            [0]=>
                            array(1) {
                              [0]=>
                              array(1) {
                                [0]=>
                                array(1) {
                                  [0]=>
                                  array(1) {
                                    [0]=>
                                    array(1) {
                                      [0]=>
                                      string(12) "Not too deep"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
ENCODE: FROM OBJECT
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
ENCODE: FROM ARRAY
[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
DECODE AGAIN: AS OBJECT
array(1) {
  [0]=>
  array(1) {
    [0]=>
    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          array(1) {
            [0]=>
            array(1) {
              [0]=>
              array(1) {
                [0]=>
                array(1) {
                  [0]=>
                  array(1) {
                    [0]=>
                    array(1) {
                      [0]=>
                      array(1) {
                        [0]=>
                        array(1) {
                          [0]=>
                          array(1) {
                            [0]=>
                            array(1) {
                              [0]=>
                              array(1) {
                                [0]=>
                                array(1) {
                                  [0]=>
                                  array(1) {
                                    [0]=>
                                    array(1) {
                                      [0]=>
                                      string(12) "Not too deep"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
DECODE AGAIN: AS ARRAY
array(1) {
  [0]=>
  array(1) {
    [0]=>
    array(1) {
      [0]=>
      array(1) {
        [0]=>
        array(1) {
          [0]=>
          array(1) {
            [0]=>
            array(1) {
              [0]=>
              array(1) {
                [0]=>
                array(1) {
                  [0]=>
                  array(1) {
                    [0]=>
                    array(1) {
                      [0]=>
                      array(1) {
                        [0]=>
                        array(1) {
                          [0]=>
                          array(1) {
                            [0]=>
                            array(1) {
                              [0]=>
                              array(1) {
                                [0]=>
                                array(1) {
                                  [0]=>
                                  array(1) {
                                    [0]=>
                                    array(1) {
                                      [0]=>
                                      string(12) "Not too deep"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}


--TEST--
JSON (http://www.crockford.com/JSON/JSON_checker/test/pass1.json)
--INI--
serialize_precision=-1
--FILE--
<?php

$test = "
[
    \"JSON Test Pattern pass1\",
    {\"object with 1 member\":[\"array with 1 element\"]},
    {},
    [],
    -42,
    true,
    false,
    null,
    {
        \"integer\": 1234567890,
        \"real\": -9876.543210,
        \"e\": 0.123456789e-12,
        \"E\": 1.234567890E+34,
        \"\":  23456789012E666,
        \"zero\": 0,
        \"one\": 1,
        \"space\": \" \",
        \"quote\": \"\\\"\",
        \"backslash\": \"\\\\\",
        \"controls\": \"\\b\\f\\n\\r\\t\",
        \"slash\": \"/ & \\/\",
        \"alpha\": \"abcdefghijklmnopqrstuvwyz\",
        \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\",
        \"digit\": \"0123456789\",
        \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\",
        \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\",
        \"true\": true,
        \"false\": false,
        \"null\": null,
        \"array\":[  ],
        \"object\":{  },
        \"address\": \"50 St. James Street\",
        \"url\": \"http://www.JSON.org/\",
        \"comment\": \"// /* <!-- --\",
        \"# -- --> */\": \" \",
        \" s p a c e d \" :[1,2 , 3

,

4 , 5        ,          6           ,7        ],
        \"compact\": [1,2,3,4,5,6,7],
        \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\",
        \"quotes\": \"" \\u0022 %22 0x22 034 "\",
        \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\"
: \"A key can be any string\"
    },
    0.5 ,98.6
,
99.44
,

1066

,\"rosebud\"]
";

echo 'Testing:' . $test . "\n";
echo "DECODE: AS OBJECT\n";
$obj = json_decode($test);
var_dump($obj);
echo "DECODE: AS ARRAY\n";
$arr = json_decode($test, true);
var_dump($arr);

echo "ENCODE: FROM OBJECT\n";
$obj_enc = json_encode($obj, JSON_PARTIAL_OUTPUT_ON_ERROR);
echo $obj_enc . "\n";
echo "ENCODE: FROM ARRAY\n";
$arr_enc = json_encode($arr, JSON_PARTIAL_OUTPUT_ON_ERROR);
echo $arr_enc . "\n";

echo "DECODE AGAIN: AS OBJECT\n";
$obj = json_decode($obj_enc);
var_dump($obj);
echo "DECODE AGAIN: AS ARRAY\n";
$arr = json_decode($arr_enc, true);
var_dump($arr);

?>
--EXPECT--
Testing:
[
    "JSON Test Pattern pass1",
    {"object with 1 member":["array with 1 element"]},
    {},
    [],
    -42,
    true,
    false,
    null,
    {
        "integer": 1234567890,
        "real": -9876.543210,
        "e": 0.123456789e-12,
        "E": 1.234567890E+34,
        "":  23456789012E666,
        "zero": 0,
        "one": 1,
        "space": " ",
        "quote": "\"",
        "backslash": "\\",
        "controls": "\b\f\n\r\t",
        "slash": "/ & \/",
        "alpha": "abcdefghijklmnopqrstuvwyz",
        "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
        "digit": "0123456789",
        "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
        "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
        "true": true,
        "false": false,
        "null": null,
        "array":[  ],
        "object":{  },
        "address": "50 St. James Street",
        "url": "http://www.JSON.org/",
        "comment": "// /* <!-- --",
        "# -- --> */": " ",
        " s p a c e d " :[1,2 , 3

,

4 , 5        ,          6           ,7        ],
        "compact": [1,2,3,4,5,6,7],
        "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
        "quotes": "" \u0022 %22 0x22 034 "",
        "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
    },
    0.5 ,98.6
,
99.44
,

1066


/*
   +----------------------------------------------------------------------+
   | Copyright (c) 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:           |
   | https://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.               |
   +----------------------------------------------------------------------+
   | Authors: Frank Denis <jedisct1@php.net>                              |
   +----------------------------------------------------------------------+
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "php.h"
#include "ext/standard/info.h"
#include "php_libsodium.h"
#include "zend_attributes.h"
#include "zend_exceptions.h"

#include <sodium.h>
#include <stdint.h>
#include <string.h>

#define PHP_SODIUM_ZSTR_TRUNCATE(zs, len) do { ZSTR_LEN(zs) = (len); } while(0)

static zend_class_entry *sodium_exception_ce;

#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64__) || defined(__i386__) || \
	 defined(_M_AMD64) || defined(_M_IX86) || defined(__aarch64__) || defined(_M_ARM64))
# define HAVE_AESGCM 1
#endif

static zend_always_inline zend_string *zend_string_checked_alloc(size_t len, int persistent)
{
	zend_string *zs;

	if (ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)) < len) {
		zend_error_noreturn(E_ERROR, "Memory allocation too large (%zu bytes)", len);
	}
	zs = zend_string_alloc(len, persistent);
	ZSTR_VAL(zs)[len] = 0;

	return zs;
}

#ifndef crypto_kdf_BYTES_MIN
# define crypto_kdf_BYTES_MIN 16
# define crypto_kdf_BYTES_MAX 64
# define crypto_kdf_CONTEXTBYTES 8
# define crypto_kdf_KEYBYTES 32
#endif

#ifndef crypto_kx_SEEDBYTES
# define crypto_kx_SEEDBYTES 32
# define crypto_kx_SESSIONKEYBYTES 32
# define crypto_kx_PUBLICKEYBYTES 32
# define crypto_kx_SECRETKEYBYTES 32
#endif

#include "libsodium_arginfo.h"

#ifndef crypto_aead_chacha20poly1305_IETF_KEYBYTES
# define crypto_aead_chacha20poly1305_IETF_KEYBYTES crypto_aead_chacha20poly1305_KEYBYTES
#endif
#ifndef crypto_aead_chacha20poly1305_IETF_NSECBYTES
# define crypto_aead_chacha20poly1305_IETF_NSECBYTES crypto_aead_chacha20poly1305_NSECBYTES
#endif
#ifndef crypto_aead_chacha20poly1305_IETF_ABYTES
# define crypto_aead_chacha20poly1305_IETF_ABYTES crypto_aead_chacha20poly1305_ABYTES
#endif

#if defined(crypto_secretstream_xchacha20poly1305_ABYTES) && SODIUM_LIBRARY_VERSION_MAJOR < 10
# undef crypto_secretstream_xchacha20poly1305_ABYTES
#endif

#ifndef crypto_pwhash_OPSLIMIT_MIN
# define crypto_pwhash_OPSLIMIT_MIN crypto_pwhash_OPSLIMIT_INTERACTIVE
#endif
#ifndef crypto_pwhash_MEMLIMIT_MIN
# define crypto_pwhash_MEMLIMIT_MIN crypto_pwhash_MEMLIMIT_INTERACTIVE
#endif
#ifndef crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN
# define crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE
#endif
#ifndef crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN
# define crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE
#endif

/* Load after the "standard" module in order to give it
 * priority in registering argon2i/argon2id password hashers.
 */
static const zend_module_dep sodium_deps[] = {
	ZEND_MOD_REQUIRED("standard")
	ZEND_MOD_END
};

zend_module_entry sodium_module_entry = {
	STANDARD_MODULE_HEADER_EX,
	NULL,
	sodium_deps,
	"sodium",
	ext_functions,
	PHP_MINIT(sodium),
	PHP_MSHUTDOWN(sodium),
	NULL,
	NULL,
	PHP_MINFO(sodium),
	PHP_SODIUM_VERSION,
	STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_SODIUM
ZEND_GET_MODULE(sodium)
#endif

/* Remove argument information from backtrace to prevent information leaks */
static void sodium_remove_param_values_from_backtrace(zend_object *obj) {
	zval rv;
	zval *trace = zend_read_property_ex(zend_get_exception_base(obj), obj, ZSTR_KNOWN(ZEND_STR_TRACE), /* silent */ false, &rv);
	if (trace && Z_TYPE_P(trace) == IS_ARRAY) {
		zval *frame;
		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(trace), frame) {
			if (Z_TYPE_P(frame) == IS_ARRAY) {
				zval *args = zend_hash_find(Z_ARRVAL_P(frame), ZSTR_KNOWN(ZEND_STR_ARGS));
				if (args) {
					zval_ptr_dtor(args);
					ZVAL_EMPTY_ARRAY(args);
				}
			}
		} ZEND_HASH_FOREACH_END();
	}
}

static zend_object *sodium_exception_create_object(zend_class_entry *ce) {
	zend_object *obj = zend_ce_exception->create_object(ce);
	sodium_remove_param_values_from_backtrace(obj);
	return obj;
}

static void sodium_separate_string(zval *zv) {
	ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
	if (!Z_REFCOUNTED_P(zv) || Z_REFCOUNT_P(zv) > 1) {
		zend_string *copy = zend_string_init(Z_STRVAL_P(zv), Z_STRLEN_P(zv), 0);
		Z_TRY_DELREF_P(zv);
		ZVAL_STR(zv, copy);
	}
}

PHP_MINIT_FUNCTION(sodium)
{
	if (sodium_init() < 0) {
		zend_error_noreturn(E_ERROR, "sodium_init()");
	}

	sodium_exception_ce = register_class_SodiumException(zend_ce_exception);
	sodium_exception_ce->create_object = sodium_exception_create_object;

#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6)
	if (FAILURE == PHP_MINIT(sodium_password_hash)(INIT_FUNC_ARGS_PASSTHRU)) {
		return FAILURE;
	}
#endif

	register_libsodium_symbols(module_number);

	return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(sodium)
{
	randombytes_close();
	return SUCCESS;
}

PHP_MINFO_FUNCTION(sodium)
{
	php_info_print_table_start();
	php_info_print_table_row(2, "sodium support", "enabled");
	php_info_print_table_row(2, "libsodium headers version", SODIUM_VERSION_STRING);
	php_info_print_table_row(2, "libsodium library version", sodium_version_string());
	php_info_print_table_end();
}

PHP_FUNCTION(sodium_memzero)
{
	zval      *buf_zv;

	if (zend_parse_parameters(ZEND_NUM_ARGS(),
									"z", &buf_zv) == FAILURE) {
		sodium_remove_param_values_from_backtrace(EG(exception));
		RETURN_THROWS();
	}
	ZVAL_DEREF(buf_zv);
	if (Z_TYPE_P(buf_zv) != IS_STRING) {
		zend_throw_exception(sodium_exception_ce, "a PHP string is required", 0);
		RETURN_THROWS();
	}
	if (Z_REFCOUNTED_P(buf_zv) && Z_REFCOUNT_P(buf_zv) == 1) {
		char *buf = Z_STRVAL(*buf_zv);
		size_t buf_len = Z_STRLEN(*buf_zv);
		if (buf_len > 0) {
			sodium_memzero(buf, (size_t) buf_len);
		}
	}
	convert_to_null(buf_zv);
}

PHP_FUNCTION(sodium_increment)
{
	zval          *val_zv;
	unsigned char *val;
	size_t         val_len;

	if (zend_parse_parameters(ZEND_NUM_ARGS(),
									"z", &val_zv) == FAILURE) {
		sodium_remove_param_values_from_backtrace(EG(exception));
		RETURN_THROWS();
	}
	ZVAL_DEREF(val_zv);
	if (Z_TYPE_P(val_zv) != IS_STRING) {
		zend_throw_exception(sodium_exception_ce, "a PHP string is required", 0);
		RETURN_THROWS();
	}

	sodium_separate_string(val_zv);
	val = (unsigned char *) Z_STRVAL(*val_zv);
	val_len = Z_STRLEN(*val_zv);
	sodium_increment(val, val_len);
}


/*
   +----------------------------------------------------------------------+
   | Copyright (c) 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:           |
   | https://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.               |
   +----------------------------------------------------------------------+
   | Authors: Frank Denis <jedisct1@php.net>                              |
   +----------------------------------------------------------------------+
*/

#ifndef PHP_LIBSODIUM_H
#define PHP_LIBSODIUM_H

extern zend_module_entry sodium_module_entry;
#define phpext_sodium_ptr &sodium_module_entry

#define PHP_SODIUM_VERSION PHP_VERSION

#ifdef ZTS
# include "TSRM.h"
#endif

#include <sodium.h>

#define SODIUM_LIBRARY_VERSION() (char *) (void *) sodium_version_string()

#define SODIUM_CRYPTO_BOX_KEYPAIRBYTES() crypto_box_SECRETKEYBYTES + crypto_box_PUBLICKEYBYTES

#define SODIUM_CRYPTO_KX_KEYPAIRBYTES() crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES

#define SODIUM_CRYPTO_SIGN_KEYPAIRBYTES() crypto_sign_SECRETKEYBYTES + crypto_sign_PUBLICKEYBYTES

#if SODIUM_LIBRARY_VERSION_MAJOR > 9 || (SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 6)

/**
 * MEMLIMIT is normalized to KB even though sodium uses Bytes in order to
 * present a consistent user-facing API.
 *
 * Threads are fixed at 1 by libsodium.
 *
 * When updating these values, synchronize ext/standard/php_password.h values.
 */
#if defined(PHP_PASSWORD_ARGON2_MEMORY_COST)
#define PHP_SODIUM_PWHASH_MEMLIMIT PHP_PASSWORD_ARGON2_MEMORY_COST
#else
#define PHP_SODIUM_PWHASH_MEMLIMIT (64 << 10)
#endif
#if defined(PHP_PASSWORD_ARGON2_TIME_COST)
#define PHP_SODIUM_PWHASH_OPSLIMIT PHP_PASSWORD_ARGON2_TIME_COST
#else
#define PHP_SODIUM_PWHASH_OPSLIMIT 4
#endif
#if defined(PHP_SODIUM_PWHASH_THREADS)
#define PHP_SODIUM_PWHASH_THREADS PHP_SODIUM_PWHASH_THREADS
#else
#define PHP_SODIUM_PWHASH_THREADS 1
#endif

#endif

PHP_MINIT_FUNCTION(sodium);
PHP_MINIT_FUNCTION(sodium_password_hash);
PHP_MSHUTDOWN_FUNCTION(sodium);
PHP_RINIT_FUNCTION(sodium);
PHP_RSHUTDOWN_FUNCTION(sodium);
PHP_MINFO_FUNCTION(sodium);

#endif	/* PHP_LIBSODIUM_H */


PHP_ARG_WITH([sodium],
  [for sodium support],
  [AS_HELP_STRING([--with-sodium],
    [Include sodium support])])

if test "$PHP_SODIUM" != "no"; then
  PKG_CHECK_MODULES([LIBSODIUM], [libsodium >= 1.0.8])

  PHP_EVAL_INCLINE([$LIBSODIUM_CFLAGS])
  PHP_EVAL_LIBLINE([$LIBSODIUM_LIBS], [SODIUM_SHARED_LIBADD])

  AC_DEFINE([HAVE_LIBSODIUMLIB], [1],
    [Define to 1 if the PHP extension 'sodium' is available.])

  SODIUM_COMPILER_FLAGS=$LIBSODIUM_CFLAGS

  dnl Add -Wno-type-limits and -Wno-logical-op as this may arise on 32bits platforms
  AC_CHECK_SIZEOF([long])
  AS_IF([test "$ac_cv_sizeof_long" -eq 4], [
    SODIUM_COMPILER_FLAGS="$SODIUM_COMPILER_FLAGS -Wno-type-limits"
    AX_CHECK_COMPILE_FLAG([-Wno-logical-op],
      [SODIUM_COMPILER_FLAGS="$SODIUM_COMPILER_FLAGS -Wno-logical-op"],,
      [-Werror])
  ])

  PHP_NEW_EXTENSION([sodium],
    [libsodium.c sodium_pwhash.c],
    [$ext_shared],,
    [$SODIUM_COMPILER_FLAGS])
  PHP_INSTALL_HEADERS([ext/sodium], [php_libsodium.h])
  PHP_SUBST([SODIUM_SHARED_LIBADD])
fi


--TEST--
Check for sodium presence
--EXTENSIONS--
sodium
--FILE--
<?php
echo "sodium extension is available";
/*
        you can add regression tests for your extension here

  the output of your test code has to be equal to the
  text in the--EXPECT-- section below for the tests
  to pass, differences between the output and the
  expected text are interpreted as failure
*/
?>
--EXPECT--
sodium extension is available


--TEST--
PDO_DBLIB: Uniqueidentifier column data type stringifying
--EXTENSIONS--
pdo_dblib
--SKIPIF--
<?php
require __DIR__ . '/config.inc';
$db = getDbConnection();
if (in_array($db->getAttribute(PDO::DBLIB_ATTR_TDS_VERSION), ['4.2', '4.6'])) die('skip feature unsupported by this TDS version');
?>
--FILE--
<?php
require __DIR__ . '/config.inc';

$db = getDbConnection();

$testGUID = '82A88958-672B-4C22-842F-216E2B88E72A';
$testGUIDBinary = base64_decode('WImogitnIkyELyFuK4jnKg==');

$sql = "SELECT CAST('$testGUID' as uniqueidentifier) as [guid]";

//--------------------------------------------------------------------------------
// 1. Get and Set the attribute
//--------------------------------------------------------------------------------
$db->setAttribute(PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER, true);
var_dump(true === $db->getAttribute(PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER));
$db->setAttribute(PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER, false);
var_dump(false === $db->getAttribute(PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER));

//--------------------------------------------------------------------------------
// 2. Binary
//--------------------------------------------------------------------------------
$stmt = $db->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);

var_dump($row['guid'] === $testGUIDBinary);

//--------------------------------------------------------------------------------
// 3. PDO::ATTR_STRINGIFY_FETCHES must not affect `uniqueidentifier` representation
//--------------------------------------------------------------------------------
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
$stmt = $db->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);

var_dump($row['guid'] === $testGUIDBinary);

//--------------------------------------------------------------------------------
// 4. Stringifying
// ! With TDS protocol version <7.0 binary will be returned and the test will fail !
// TODO: something from PDO::ATTR_SERVER_VERSION, PDO::ATTR_CLIENT_VERSION or PDO::ATTR_SERVER_INFO should be used
// to get TDS version and skip this test in this case.
//--------------------------------------------------------------------------------
$db->setAttribute(PDO::DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER, true);
$stmt = $db->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);

var_dump($row['guid'] === $testGUID);
var_dump($row['guid']);

?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
string(36) "82A88958-672B-4C22-842F-216E2B88E72A"


--TEST--
rfc1867 empty upload
--INI--
file_uploads=1
upload_max_filesize=1024
max_file_uploads=10
--POST_RAW--
Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737
-----------------------------20896060251896012921717172737
Content-Disposition: form-data; name="foo"

-----------------------------20896060251896012921717172737
Content-Disposition: form-data; name="file1"; filename="file1.txt"
Content-Type: text/plain-file1

1
-----------------------------20896060251896012921717172737
Content-Disposition: form-data; name="file2"; filename=""
Content-Type: text/plain-file2

-----------------------------20896060251896012921717172737
Content-Disposition: form-data; name="file3"; filename="file3.txt"
Content-Type: text/plain-file3

3
-----------------------------20896060251896012921717172737--
--FILE--
<?php
var_dump($_FILES);
var_dump($_POST);
if (is_uploaded_file($_FILES["file1"]["tmp_name"])) {
    var_dump(file_get_contents($_FILES["file1"]["tmp_name"]));
}
if (is_uploaded_file($_FILES["file3"]["tmp_name"])) {
    var_dump(file_get_contents($_FILES["file3"]["tmp_name"]));
}
?>
--EXPECTF--
array(3) {
  ["file1"]=>
  array(6) {
    ["name"]=>
    string(9) "file1.txt"
    ["full_path"]=>
    string(9) "file1.txt"
    ["type"]=>
    string(16) "text/plain-file1"
    ["tmp_name"]=>
    string(%d) "%s"
    ["error"]=>
    int(0)
    ["size"]=>
    int(1)
  }
  ["file2"]=>
  array(6) {
    ["name"]=>
    string(0) ""
    ["full_path"]=>
    string(0) ""
    ["type"]=>
    string(0) ""
    ["tmp_name"]=>
    string(0) ""
    ["error"]=>
    int(4)
    ["size"]=>
    int(0)
  }
  ["file3"]=>
  array(6) {
    ["name"]=>
    string(9) "file3.txt"
    ["full_path"]=>
    string(9) "file3.txt"
    ["type"]=>
    string(16) "text/plain-file3"
    ["tmp_name"]=>
    string(%d) "%s"
    ["error"]=>
    int(0)
    ["size"]=>
    int(1)
  }
}
array(1) {
  ["foo"]=>
  string(0) ""
}
string(1) "1"
string(1) "3"


--TEST--
short_open_tag: Off
--INI--
short_open_tag=off
--FILE--
<%= 'so should this' %>

<?php
$a = 'This gets echoed twice';
?>

<?= $a?>

<%= $a%>

<? $b=3; ?>

<?php
   echo "{$b}";
?>
<?= "{$b}"?>
--EXPECTF--
<%= 'so should this' %>

This gets echoed twice
<%= $a%>

<? $b=3; ?>

Warning: Undefined variable $b in %s on line %d

Warning: Undefined variable $b in %s on line %d


--TEST--
short_open_tag: Off
--INI--
short_open_tag=off
--FILE--
<?
echo "Used a short tag\n";
?>
Finished
--EXPECT--
<?
echo "Used a short tag\n";
?>
Finished


/** @return DocCommentTag[] */
function parseDocComments(array $comments): array {
    $tags = [];
    foreach ($comments as $comment) {
        if ($comment instanceof DocComment) {
            $tags = array_merge($tags, parseDocComment($comment));
        }
    }

    return $tags;
}

/** @return DocCommentTag[] */
function parseDocComment(DocComment $comment): array {
    $commentText = substr($comment->getText(), 2, -2);
    $tags = [];
    foreach (explode("\n", $commentText) as $commentLine) {
        $regex = '/^\*\s*@([a-z-]+)(?:\s+(.+))?$/';
        if (preg_match($regex, trim($commentLine), $matches)) {
            $tags[] = new DocCommentTag($matches[1], $matches[2] ?? null);
        }
    }

    return $tags;
}

class FramelessFunctionInfo {
    public int $arity;
}

function parseFramelessFunctionInfo(string $json): FramelessFunctionInfo {
    // FIXME: Should have some validation
    $json = json_decode($json, true);
    $framelessFunctionInfo = new FramelessFunctionInfo();
    $framelessFunctionInfo->arity = $json["arity"];
    return $framelessFunctionInfo;
}

function parseFunctionLike(
    PrettyPrinterAbstract $prettyPrinter,
    FunctionOrMethodName $name,
    int $classFlags,
    int $flags,
    Node\FunctionLike $func,
    ?string $cond,
    bool $isUndocumentable,
    ?int $minimumPhpVersionIdCompatibility
): FuncInfo {
    try {
        $comments = $func->getComments();
        $paramMeta = [];
        $aliasType = null;
        $alias = null;
        $isDeprecated = false;
        $supportsCompileTimeEval = false;
        $verify = true;
        $docReturnType = null;
        $tentativeReturnType = false;
        $docParamTypes = [];
        $refcount = null;
        $framelessFunctionInfos = [];

        if ($comments) {
            $tags = parseDocComments($comments);

            foreach ($tags as $tag) {
                switch ($tag->name) {
                    case 'alias':
                    case 'implementation-alias':
                        $aliasType = $tag->name;
                        $aliasParts = explode("::", $tag->getValue());
                        if (count($aliasParts) === 1) {
                            $alias = new FunctionName(new Name($aliasParts[0]));
                        } else {
                            $alias = new MethodName(new Name($aliasParts[0]), $aliasParts[1]);
                        }
                        break;

                    case 'deprecated':
                        $isDeprecated = true;
                        break;

                    case 'no-verify':
                        $verify = false;
                        break;

                    case 'tentative-return-type':
                        $tentativeReturnType = true;
                        break;

                    case 'return':
                        $docReturnType = $tag->getType();
                        break;

                    case 'param':
                        $docParamTypes[$tag->getVariableName()] = $tag->getType();
                        break;

                    case 'refcount':
                        $refcount = $tag->getValue();
                        break;

                    case 'compile-time-eval':
                        $supportsCompileTimeEval = true;
                        break;

                    case 'prefer-ref':
                        $varName = $tag->getVariableName();
                        if (!isset($paramMeta[$varName])) {
                            $paramMeta[$varName] = [];
                        }
                        $paramMeta[$varName][$tag->name] = true;
                        break;

                    case 'undocumentable':
                        $isUndocumentable = true;
                        break;

                    case 'frameless-function':
                        $framelessFunctionInfos[] = parseFramelessFunctionInfo($tag->getValue());
                        break;
                }
            }
        }

        $varNameSet = [];
        $args = [];
        $numRequiredArgs = 0;
        $foundVariadic = false;
        foreach ($func->getParams() as $i => $param) {
            if ($param->isPromoted()) {
                throw new Exception("Promoted properties are not supported");
            }

            $varName = $param->var->name;
            $preferRef = !empty($paramMeta[$varName]['prefer-ref']);
            unset($paramMeta[$varName]);

            if (isset($varNameSet[$varName])) {
                throw new Exception("Duplicate parameter name $varName");
            }
            $varNameSet[$varName] = true;

            if ($preferRef) {
                $sendBy = ArgInfo::SEND_PREFER_REF;
            } else if ($param->byRef) {
                $sendBy = ArgInfo::SEND_BY_REF;
            } else {
                $sendBy = ArgInfo::SEND_BY_VAL;
            }

            if ($foundVariadic) {
                throw new Exception("Only the last parameter can be variadic");
            }

            $type = $param->type ? Type::fromNode($param->type) : null;
            if ($type === null && !isset($docParamTypes[$varName])) {
                throw new Exception("Missing parameter type");
            }

            if ($param->default instanceof Expr\ConstFetch &&
                $param->default->name->toLowerString() === "null" &&
                $type && !$type->isNullable()
            ) {
                $simpleType = $type->tryToSimpleType();
                if ($simpleType === null || !$simpleType->isMixed()) {
                    throw new Exception("Parameter $varName has null default, but is not nullable");
                }
            }

            if ($param->default instanceof Expr\ClassConstFetch && $param->default->class->toLowerString() === "self") {
                throw new Exception('The exact class name must be used instead of "self"');
            }

            $foundVariadic = $param->variadic;

            $args[] = new ArgInfo(
                $varName,
                $sendBy,
                $param->variadic,
                $type,
                isset($docParamTypes[$varName]) ? Type::fromString($docParamTypes[$varName]) : null,
                $param->default ? $prettyPrinter->prettyPrintExpr($param->default) : null,
                createAttributes($param->attrGroups)
            );
            if (!$param->default && !$param->variadic) {
                $numRequiredArgs = $i + 1;
            }
        }

        foreach (array_keys($paramMeta) as $var) {
            throw new Exception("Found metadata for invalid param $var");
        }

        $returnType = $func->getReturnType();
        if ($returnType === null && $docReturnType === null && !$name->isConstructor() && !$name->isDestructor()) {
            throw new Exception("Missing return type");
        }

        $return = new ReturnInfo(
            $func->returnsByRef(),
            $returnType ? Type::fromNode($returnType) : null,
            $docReturnType ? Type::fromString($docReturnType) : null,
            $tentativeReturnType,
            $refcount
        );

        return new FuncInfo(
            $name,
            $classFlags,
            $flags,
            $aliasType,
            $alias,
            $isDeprecated,
            $supportsCompileTimeEval,
            $verify,
            $args,
            $return,
            $numRequiredArgs,
            $cond,
            $isUndocumentable,
            $minimumPhpVersionIdCompatibility,
            createAttributes($func->attrGroups),
            $framelessFunctionInfos,
            createExposedDocComment($comments)
        );
    } catch (Exception $e) {
        throw new Exception($name . "(): " .$e->getMessage());
    }
}

/**
 * @param array<int, array<int, AttributeGroup> $attributes
 */
function parseConstLike(
    PrettyPrinterAbstract $prettyPrinter,
    ConstOrClassConstName $name,
    Node\Const_ $const,
    int $flags,
    ?Node $type,
    array $comments,
    ?string $cond,
    bool $isUndocumentable,
    ?int $phpVersionIdMinimumCompatibility,
    array $attributes
): ConstInfo {
    $phpDocType = null;
    $deprecated = false;
    $cValue = null;
    $link = null;
    $isFileCacheAllowed = true;
    if ($comments) {
        $tags = parseDocComments($comments);
        foreach ($tags as $tag) {
            if ($tag->name === 'var') {
                $phpDocType = $tag->getType();
            } elseif ($tag->name === 'deprecated') {
                $deprecated = true;
            } elseif ($tag->name === 'cvalue') {
                $cValue = $tag->value;
            } elseif ($tag->name === 'undocumentable') {
                $isUndocumentable = true;
            } elseif ($tag->name === 'link') {
                $link = $tag->value;
            } elseif ($tag->name === 'no-file-cache') {
                $isFileCacheAllowed = false;
            }
        }
    }

    if ($type === null && $phpDocType === null) {
        throw new Exception("Missing type for constant " . $name->__toString());
    }

    $constType = $type ? Type::fromNode($type) : null;
    $constPhpDocType = $phpDocType ? Type::fromString($phpDocType) : null;


--TEST--
Post a file
--SKIPIF--
<?php
include "skipif.inc";
?>
--FILE--
<?php
include "php_cli_server.inc";
php_cli_server_start('var_dump($_FILES);');

$host = PHP_CLI_SERVER_HOSTNAME;
$fp = php_cli_server_connect();

$post_data = <<<POST
-----------------------------114782935826962
Content-Disposition: form-data; name="userfile"; filename="laruence.txt"
Content-Type: text/plain

I am not sure about this.

-----------------------------114782935826962--

POST;

$post_len = strlen($post_data);

if(fwrite($fp, <<<HEADER
POST / HTTP/1.1
Host: {$host}
Content-Type: multipart/form-data; boundary=---------------------------114782935826962
Content-Length: {$post_len}

{$post_data}
HEADER
)) {
    while (!feof($fp)) {
        echo fgets($fp);
    }
}

?>
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8

array(1) {
  ["userfile"]=>
  array(6) {
    ["name"]=>
    string(12) "laruence.txt"
    ["full_path"]=>
    string(12) "laruence.txt"
    ["type"]=>
    string(10) "text/plain"
    ["tmp_name"]=>
    string(%d) "%s"
    ["error"]=>
    int(0)
    ["size"]=>
    int(26)
  }
}