private function paramType(
        $value,
        $givenType = null,
        $definition = null,
        $allowMixedArrayType = false
    ) {
        $valueType = gettype($value);

        // If a definition is provided, the type is set to `array` to force
        // the value to be interpreted as an array or a struct, even if null.
        if ($definition !== null) {
            $valueType = 'array';
        }

        switch ($valueType) {
            case 'boolean':
                $type = $this->typeObject($givenType ?: self::TYPE_BOOL);
                break;

            case 'integer':
                $value = (string) $value;
                $type = $this->typeObject($givenType ?: self::TYPE_INT64);
                break;

            case 'double':
                $type = $this->typeObject($givenType ?: self::TYPE_FLOAT64);
                switch ($value) {
                    case INF:
                        $value = 'Infinity';
                        break;

                    case -INF:
                        $value = '-Infinity';
                        break;
                }

                if (!is_string($value) && is_nan($value)) {
                    $value = 'NaN';
                }

                break;

            case 'string':
                $type = $this->typeObject($givenType ?: self::TYPE_STRING);
                break;

            case 'resource':
                $type = $this->typeObject($givenType ?: self::TYPE_BYTES);
                $value = base64_encode(stream_get_contents($value));
                break;

            case 'object':
                list ($type, $value) = $this->objectParam($value);
                break;

            case 'array':
                if ($givenType === Database::TYPE_STRUCT) {
                    if (!($definition instanceof StructType)) {
                        throw new \InvalidArgumentException(
                            'Struct parameter types must be declared explicitly, and must '.
                            'be an instance of Google\Cloud\Spanner\StructType.'
                        );
                    }

                    if ($value instanceof \stdClass) {
                        $value = (array) $value;
                    }

                    list ($value, $type) = $this->structParam($value, $definition);
                } else {
                    if (!($definition instanceof ArrayType)) {
                        throw new \InvalidArgumentException(
                            'Array parameter types must be an instance of Google\Cloud\Spanner\ArrayType.'
                        );
                    }

                    list ($value, $type) = $this->arrayParam($value, $definition, $allowMixedArrayType);
                }

                break;

            case 'NULL':
                $type = $this->typeObject($givenType);
                break;

            default:
                throw new \InvalidArgumentException(sprintf(
                    'Unrecognized value type %s. ' .
                    'Please ensure you are using the latest version of google/cloud or google/cloud-spanner.',
                    get_class($value)
                ));
                break;
        }

        return [$value, $type];
    }