- allow to avoid deps on shared extensions on build time
authorMichael Wallner <mike@php.net>
Tue, 6 Jun 2006 21:43:22 +0000 (21:43 +0000)
committerMichael Wallner <mike@php.net>
Tue, 6 Jun 2006 21:43:22 +0000 (21:43 +0000)
- upgrade xmlrpc tools

12 files changed:
config.m4
http.c
http_functions.c
http_message_object.c
http_querystring_api.c
http_querystring_object.c
lib/XmlRpcClient.php
lib/XmlRpcServer.php [new file with mode: 0644]
php_http.h
php_http_cache_api.h
php_http_querystring_api.h
php_http_querystring_object.h

index 0339acc3e74177b5cd3f6f71e0a8197cf69b9271..0fc8c7f6c774f25eb3fc85914f10b6c0881571c8 100644 (file)
--- a/config.m4
+++ b/config.m4
@@ -13,6 +13,9 @@ PHP_ARG_WITH([http-zlib-compression], [whether to enable zlib encodings support]
 PHP_ARG_WITH([http-magic-mime], [whether to enable response content type guessing],
 [  --with-http-magic-mime[=LIBMAGICDIR]
                            HTTP: with magic mime response content type guessing], "no", "no")
+PHP_ARG_WITH([http-shared-deps], [whether to depend on shared extensions],
+[  --with-http-shared-deps HTTP: disable to not depend on shared extensions
+                           like SPL, hash, iconv and session], $PHP_HTTP, $PHP_HTTP)
 
 if test "$PHP_HTTP" != "no"; then
 
@@ -31,6 +34,12 @@ if test "$PHP_HTTP" != "no"; then
                ])
        ])
 
+       if test "PHP_HTTP_SHARED_DEPS" != "no"; then
+               AC_DEFINE([HTTP_SHARED_DEPS], [1], [ ])
+       else
+               AC_DEFINE([HTTP_SHARED_DEPS], [0], [ ])
+       endif
+
 dnl -------
 dnl HEADERS
 dnl -------
diff --git a/http.c b/http.c
index 9322e3fd54feb30432c07cce7b5488a9780910f8..843db2284c703ba0b3cbd38f37b62af95205427b 100644 (file)
--- a/http.c
+++ b/http.c
@@ -137,16 +137,16 @@ PHP_MINFO_FUNCTION(http);
 /* {{{ http_module_dep */
 #if ZEND_EXTENSION_API_NO >= 220050617
 static zend_module_dep http_module_deps[] = {
-#      ifdef HAVE_SPL
+#      if defined(HAVE_SPL) && !HTTP_SHARED_EXT(SPL)
        ZEND_MOD_REQUIRED("spl")
 #      endif
-#      ifdef HTTP_HAVE_EXT_HASH
+#      if defined(HTTP_HAVE_EXT_HASH) && !HTTP_SHARED_EXT(HASH)
        ZEND_MOD_REQUIRED("hash")
 #      endif
-#      ifdef HAVE_PHP_SESSION
+#      if defined(HAVE_PHP_SESSION) && !HTTP_SHARED_EXT(PHP_SESSION)
        ZEND_MOD_REQUIRED("session")
 #      endif
-#      ifdef HAVE_ICONV
+#      if defined(HAVE_ICONV) && !HTTP_SHARED_EXT(ICONV)
        ZEND_MOD_REQUIRED("iconv")
 #      endif
        {NULL, NULL, NULL, 0}
index 0462c3b760b24aea0b31bd10ac51dea9e5f050a8..bdd021b767c90decadffae1d7c4b546ae25a417b 100644 (file)
@@ -21,7 +21,7 @@
 #include "ext/standard/php_string.h"
 #include "zend_operators.h"
 
-#ifdef HAVE_PHP_SESSION
+#ifdef HAVE_PHP_SESSION && !HTTP_SHARED_EXT(PHP_SESSION)
 #      include "ext/session/php_session.h"
 #endif
 
@@ -728,7 +728,7 @@ PHP_FUNCTION(http_redirect)
                RETURN_FALSE;
        }
 
-#ifdef HAVE_PHP_SESSION
+#ifdef HAVE_PHP_SESSION && !HTTP_SHARED_EXT(PHP_SESSION)
        /* append session info */
        if (session) {
                if (!params) {
index 5e03e1e07c79ea2b751ae9ef9dfed5356400ed29..824745f460f041b99e162b18fcdc72b74a151efb 100644 (file)
 #include "php_http_request_api.h"
 #include "php_http_request_object.h"
 
-#ifndef WONKY
-#      ifdef HAVE_SPL
+#if defined(HAVE_SPL) && !HTTP_SHARED_EXT(SPL) && !defined(WONKY)
 /* SPL doesn't install its headers */
 extern PHPAPI zend_class_entry *spl_ce_Countable;
-#      endif
 #endif
 
 #define HTTP_BEGIN_ARGS(method, req_args)      HTTP_BEGIN_ARGS_EX(HttpMessage, method, 0, req_args)
@@ -187,7 +185,7 @@ PHP_MINIT_FUNCTION(http_message_object)
        HTTP_REGISTER_CLASS_EX(HttpMessage, http_message_object, NULL, 0);
        
 #ifndef WONKY
-#      ifdef HAVE_SPL
+#      if defined(HAVE_SPL) && !HTTP_SHARED_EXT(SPL)
        zend_class_implements(http_message_object_ce TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator);
 #      else
        zend_class_implements(http_message_object_ce TSRMLS_CC, 2, zend_ce_serializable, zend_ce_iterator);
index 45aa5acc7666a26cbe5575cbbd1312ce5fac9ebe..b367c20420e0e59ebf0561a0932d96520e07c5ce 100644 (file)
@@ -16,7 +16,7 @@
 #include "php_http.h"
 
 #include "php_variables.h"
-#ifdef HAVE_ICONV
+#ifdef HAVE_ICONV && !HTTP_SHARED_EXT(ICONV)
 #      undef PHP_ATOM_INC
 #      include "ext/iconv/php_iconv.h"
 #      include "ext/standard/url.h"
@@ -38,7 +38,7 @@ static inline int _http_querystring_modify_array_ex(zval *qarray, int key_type,
 static inline int _http_querystring_modify_array(zval *qarray, zval *params TSRMLS_DC);
 
 
-#ifdef HAVE_ICONV
+#ifdef HAVE_ICONV && !HTTP_SHARED_EXT(ICONV)
 PHP_HTTP_API int _http_querystring_xlate(zval *array, zval *param, const char *ie, const char *oe TSRMLS_DC)
 {
        HashPosition pos;
index d0eed53d14942490d5f43db6a464055b8930518b..73ef9d5960d94470dee7e62ba8375cc27611ced3 100644 (file)
@@ -61,7 +61,7 @@ HTTP_BEGIN_ARGS(__getter, 1)
        HTTP_ARG_VAL(delete, 0)
 HTTP_END_ARGS;
 
-#ifdef HAVE_ICONV
+#ifdef HAVE_ICONV && !HTTP_SHARED_EXT(ICONV)
 HTTP_BEGIN_ARGS(xlate, 2)
        HTTP_ARG_VAL(from_encoding, 0)
        HTTP_ARG_VAL(to_encoding, 0)
@@ -95,7 +95,7 @@ zend_function_entry http_querystring_object_fe[] = {
 #ifndef WONKY
        HTTP_QUERYSTRING_ME(singleton, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 #endif
-#ifdef HAVE_ICONV
+#ifdef HAVE_ICONV && !HTTP_SHARED_EXT(ICONV)
        HTTP_QUERYSTRING_ME(xlate, ZEND_ACC_PUBLIC)
 #endif
        
@@ -431,7 +431,7 @@ HTTP_QUERYSTRING_GETTER(getArray, IS_ARRAY);
 HTTP_QUERYSTRING_GETTER(getObject, IS_OBJECT);
 /* }}} */
 
-#ifdef HAVE_ICONV
+#ifdef HAVE_ICONV && !HTTP_SHARED_EXT(ICONV)
 /* {{{ proto bool HttpQueryString::xlate(string ie, string oe)
  *
  * Converts the query string from the source encoding ie to the target encoding oe.
index 9cdc7bbd54151115576e730556f6be20a6fb6496..83a154e933448a37883e4d5e1bf3ffbc32f63823 100644 (file)
@@ -9,10 +9,10 @@
  * Usage:
  * <code>
  * <?php
- * $rpc = new XmlRpcClient('http://mike:secret@example.com/cgi-bin/vpop-xmlrpc', 'vpop');
- * $rpc->options = array(array('compress' => true));
+ * $rpc = new XmlRpcClient('http://mike:secret@example.com/cgi-bin/vpop-xmlrpc');
+ * $rpc->__request->setOptions(array('compress' => true));
  * try {
- *     print_r($rpc->listdomain(array('domain' => 'example.com')));
+ *     print_r($rpc->vpop->listdomain(array('domain' => 'example.com')));
  * } catch (Exception $ex) {
  *     echo $ex;
  * }
  * @copyright   Michael Wallner, <mike@iworks.at>
  * @license     BSD, revised
  * @package     pecl/http
- * @version     $Revision$
+ * @version     $Revision$
  */
 class XmlRpcClient
 {
-    /**
-     * RPC namespace
-     *
-     * @var string
-     */
-    public $namespace;
-    
-    /**
-     * HttpRequest instance
-     *
-     * @var HttpRequest
-     */
-    public $request;
-
-    /**
-     * Constructor
-     *
-     * @param string $url RPC endpoint
-     * @param string $namespace RPC namespace
-     */
-    public function __construct($url, $namespace = '')
-    {
-        $this->namespace = $namespace;
-        $this->request = new HttpRequest($url, HTTP_METH_POST);
-        $this->request->setContentType('text/xml');
-    }
-
-    /**
-     * RPC method proxy
-     *
-     * @param string $method RPC method name
-     * @param array $params RPC method arguments
-     * @return mixed decoded RPC response
-     * @throws Exception
-     */
-    public function __call($method, array $params)
-    {
-        if ($this->namespace) {
-            $method = $this->namespace .'.'. $method;
-        }
-        
-        $data = xmlrpc_encode_request($method, $params);
-        $this->request->setRawPostData($data);
-        
-        $response = $this->request->send();
-        if ($response->getResponseCode() != 200) {
-            throw new Exception(
-                $response->getResponseStatus(), 
-                $response->getResponseCode()
-            );
-        }
-        $data = xmlrpc_decode($response->getBody(), 'utf-8');
-        
-        if (isset($data['faultCode'], $data['faultString'])) {
-            throw new Exception(
-                $data['faultString'],
-                $data['faultCode']
-            );
-        }
-        
-        return $data;
-    }
-    
-    public function __set($what, $params)
-    {
-        return call_user_func_array(
-            array($this->request, "set$what"),
-            $params
-        );
-    }
-    
-    public function __get($what)
-    {
-        return call_user_func(
-            array($this->request, "get$what")
-        );
-    }
+       /**
+        * RPC namespace
+        *
+        * @var string
+        */
+       public $__namespace;
+       
+       /**
+        * HttpRequest instance
+        *
+        * @var HttpRequest
+        */
+       public $__request;
+       
+       /**
+        * Client charset
+        * 
+        * @var string
+        */
+       public $__encoding = "iso-8859-1";
+       
+       /**
+        * Constructor
+        *
+        * @param string $url RPC endpoint
+        * @param string $namespace RPC namespace
+        * @param array  $options HttpRequest options
+        */
+       public function __construct($url, $namespace = '', array $options = null)
+       {
+               $this->__request = new HttpRequest($url, HTTP_METH_POST);
+               $this->__request->setOptions($options);
+               $this->__namespace = $namespace;
+       }
+       
+       /**
+        * RPC method proxy
+        *
+        * @param string $method RPC method name
+        * @param array $params RPC method arguments
+        * @return mixed decoded RPC response
+        * @throws Exception
+        */
+       public function __call($method, array $params)
+       {
+               if ($this->__namespace) {
+                       $method = $this->__namespace .'.'. $method;
+               }
+               $this->__request->setContentType("text/xml; charset=". $this->__encoding);
+               $request = xmlrpc_encode_request($method, $params, array("encoding" => $this->__encoding));
+               $this->__request->setRawPostData($request);
+               $this->__request->send();
+               $response = $this->__request->getResponseMessage();
+               if ($response->getResponseCode() != 200) {
+                       throw new Exception(
+                               $response->getResponseStatus(), 
+                               $response->getResponseCode()
+                       );
+               }
+               
+               $data = xmlrpc_decode($response->getBody(), $this->__encoding);
+               if (xmlrpc_is_fault($data)) {
+                       throw new Exception(
+                               (string) $data['faultString'],
+                               (int) $data['faultCode']
+                       );
+               }
+               
+               return $data;
+       }
+       
+       /**
+        * Returns self, where namespace is set to variable name
+        *
+        * @param string $ns
+        * @return XmlRpcRequest
+        */
+       public function __get($ns)
+       {
+               $this->__namespace = $ns;
+               return $this;
+       }
 }
 
 ?>
diff --git a/lib/XmlRpcServer.php b/lib/XmlRpcServer.php
new file mode 100644 (file)
index 0000000..e77910f
--- /dev/null
@@ -0,0 +1,237 @@
+<?php
+
+XmlRpcServer::setContentType("text/xml");
+XmlRpcServer::capture();
+
+/**
+ * XMLRPC Server, very KISS
+ * $Id$
+ * 
+ * NOTE: requires ext/xmlrpc
+ * 
+ * Usage:
+ * <code>
+ * <?php
+ *     class Handler extends XmlRpcRequestHandlerStub {
+ *             public function xmlrpcPing(array $values) {
+ *                     return true;
+ *             }
+ *     }
+ *     try {
+ *             XmlRpcServer::factory("namespace")->registerHandler(new Handler);
+ *             XmlRpcServer::run();
+ *     } catch (Exception $ex) {
+ *             XmlRpcServer::error($ex->getCode(), $ex->getMessage());
+ *  }
+ * </code>
+ * 
+ * @copyright   Michael Wallner, <mike@iworks.at>
+ * @license     BSD, revised
+ * @package     pecl/http
+ * @version     $Revision$
+ */
+
+class XmlRpcServer extends HttpResponse
+{
+       /**
+        * Server charset
+        *
+        * @var string
+        */
+       public static $encoding = "iso-8859-1";
+       
+       /**
+        * RPC namespace
+        *
+        * @var string
+        */
+       public $namespace;
+       
+       /**
+        * RPC handler attached to this server instance
+        *
+        * @var XmlRpcRequestHandler
+        */
+       protected $handler;
+       
+       private static $xmlreq;
+       private static $xmlrpc;
+       private static $refcnt = 0;
+       private static $handle = array();
+       
+       /**
+        * Create a new XmlRpcServer instance
+        *
+        * @param string $namespace
+        * @param string $encoding
+        */
+       public function __construct($namespace) {
+               $this->namespace = $namespace;
+               self::initialize();
+       }
+       
+       /**
+        * Destructor
+        */
+       public function __destruct() {
+               if (self::$refcnt && !--self::$refcnt) {
+                       xmlrpc_server_destroy(self::$xmlrpc);
+               }
+       }
+       
+       /**
+        * Static factory
+        *
+        * @param string $namespace
+        * @return XmlRpcServer
+        */
+       public static function factory($namespace) {
+               return new XmlRpcServer($namespace);
+       }
+       
+       /**
+        * Run all servers and send response
+        *
+        * @param array $options
+        */
+       public static function run(array $options = null) {
+               self::initialize(false, true);
+               HttpResponse::setContentType("text/xml; charset=". self::$encoding);
+               echo xmlrpc_server_call_method(self::$xmlrpc, self::$xmlreq, null, 
+                       array("encoding" => self::$encoding) + (array) $options);
+       }
+       
+       /**
+        * Test hook; call instead of XmlRpcServer::run()
+        *
+        * @param string $method
+        * @param array $params
+        * @param array $options
+        */
+       public static function test($method, array $params, array $options = null) {
+               self::$xmlreq = xmlrpc_encode_request($method, $params);
+               self::run();
+       }
+       
+       /**
+        * Optional XMLRPC error handler
+        *
+        * @param int $code
+        * @param string $msg
+        */
+       public static function error($code, $msg) {
+               echo xmlrpc_encode(array("faultCode" => $code, "faultString" => $msg));
+       }
+       
+       /**
+        * Register a single method
+        *
+        * @param string $name
+        * @param mixed $callback
+        * @param mixed $dispatch
+        * @param array $spec
+        */
+       public function registerMethod($name, $callback, $dispatch = null, array $spec = null) {
+               if (!is_callable($callback, false, $cb_name)) {
+                       throw new Exception("$cb_name is not a valid callback");
+               }
+               if (isset($dispatch)) {
+                       if (!is_callable($dispatch, false, $cb_name)) {
+                               throw new Exception("$cb_name is not a valid callback");
+                       }
+                       xmlrpc_server_register_method(self::$xmlrpc, $name, $dispatch);
+                       self::$handle[$name] = $callback;
+               } else {
+                       xmlrpc_server_register_method(self::$xmlrpc, $name, $callback);
+               }
+               
+               if (isset($spec)) {
+                       xmlrpc_server_add_introspection_data(self::$xmlrpc, $spec);
+               }
+       }
+       
+       /**
+        * Register an XmlRpcRequestHandler for this server instance
+        *
+        * @param XmlRpcRequestHandler $handler
+        */
+       public function registerHandler(XmlRpcRequestHandler $handler) {
+               $this->handler = $handler;
+               
+               foreach (get_class_methods($handler) as $method) {
+                       if (!strncmp($method, "xmlrpc", 6)) {
+                               $this->registerMethod(
+                                       $this->method($method, $handler->getNamespace()), 
+                                       array($handler, $method), array($this, "dispatch"));
+                       }
+               }
+               
+               $handler->getIntrospectionData($spec);
+               if (is_array($spec)) {
+                       xmlrpc_server_add_introspection_data(self::$xmlrpc, $spec);
+               }
+       }
+       
+       private function method($method, $namespace = null) {
+               if (!strlen($namespace)) {
+                       $namespace = strlen($this->namespace) ? $this->namespace : "xmlrpc";
+               }
+               return $namespace .".". strtolower($method[6]) . substr($method, 7);
+       }
+       
+       private function dispatch($method, array $params = null) {
+               if (array_key_exists($method, self::$handle)) {
+                       return call_user_func(self::$handle[$method], $params);
+               }
+               throw new Exception("Unknown XMLRPC method: $method");
+       }
+       
+       private static function initialize($server = true, $data = false) {
+               if ($data) {
+                       if (!self::$xmlreq && !(self::$xmlreq = http_get_request_body())) {
+                               throw new Exception("Failed to fetch XMLRPC request body");
+                       }
+               }
+               if ($server) {
+                       if (!self::$xmlrpc && !(self::$xmlrpc = xmlrpc_server_create())) {
+                               throw new Exception("Failed to initialize XMLRPC server");
+                       }
+                       ++self::$refcnt;
+               }
+       }
+}
+
+/**
+ * XmlRpcRequestHandler
+ * 
+ * Define XMLRPC methods with an "xmlrpc" prefix, eg:
+ * <code>
+ *     class IntOp implements XmlRpcRequestHandler {
+ *             public function getNamespace() {
+ *                     return "int";
+ *             }
+ *             public function getInstrospectionData(array &$spec = null) {
+ *             }
+ *             // XMLRPC method name: int.sumValues
+ *             public function xmlrpcSumValues(array $values) {
+ *                      return array_sum($values);
+ *             }
+ *     }
+ * </code>
+ */
+interface XmlRpcRequestHandler {
+       public function getNamespace();
+       public function getIntrospectionData(array &$spec = null);
+}
+
+/**
+ * XmlRpcRequestHandlerStub
+ */
+abstract class XmlRpcRequestHandlerStub implements XmlRpcRequestHandler {
+       public function getNamespace() {
+       }
+       public function getIntrospectionData(array &$spec = null) {
+       }
+}
+
+?>
index ab53b85de4a8cc9d6517f1352f4d519767ca69b2..f3574d322245aacf9252fb31cfcd58fd984365de 100644 (file)
@@ -144,6 +144,8 @@ ZEND_EXTERN_MODULE_GLOBALS(http);
 #      define HTTP_G (&http_globals)
 #endif
 
+#define HTTP_SHARED_EXT(EXT) (COMPILE_DL_##EXT && !HTTP_SHARED_DEPS)
+
 PHP_FUNCTION(http_test);
 PHP_FUNCTION(http_date);
 PHP_FUNCTION(http_build_url);
index 6efe1d834199cb23295f6175a112a9918cb068c0..dbbe0a2b78df9287c143b572c3e94719175cc0ee 100644 (file)
 #include "ext/standard/crc32.h"
 #include "ext/standard/sha1.h"
 #include "ext/standard/md5.h"
-#if defined(HTTP_HAVE_EXT_HASH)
-#      include "php_hash.h"
-#elif defined(HTTP_HAVE_HASH_EXT_HASH)
-#      define HTTP_HAVE_EXT_HASH
-#      include "hash/php_hash.h"
-#elif defined(HTTP_HAVE_EXT_HASH_EXT_HASH)
-#      define HTTP_HAVE_EXT_HASH
-#      include "ext/hash/php_hash.h"
+#if !HTTP_SHARED_EXT(HASH)
+#      if defined(HTTP_HAVE_EXT_HASH)
+#              include "php_hash.h"
+#      elif defined(HTTP_HAVE_HASH_EXT_HASH)
+#              define HTTP_HAVE_EXT_HASH
+#              include "hash/php_hash.h"
+#      elif defined(HTTP_HAVE_EXT_HASH_EXT_HASH)
+#              define HTTP_HAVE_EXT_HASH
+#              include "ext/hash/php_hash.h"
+#      endif
 #endif
 
 #define http_etag_digest(d, l) _http_etag_digest((d), (l))
@@ -53,7 +55,7 @@ static inline void *_http_etag_init(TSRMLS_D)
        void *ctx = NULL;
        char *mode = HTTP_G->etag.mode;
        
-#ifdef HTTP_HAVE_EXT_HASH
+#if defined(HTTP_HAVE_EXT_HASH) && !HTTP_SHARED_EXT(HASH)
        php_hash_ops *eho = NULL;
        
        if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) {
@@ -79,7 +81,7 @@ static inline char *_http_etag_finish(void *ctx TSRMLS_DC)
        unsigned char digest[128] = {0};
        char *etag = NULL, *mode = HTTP_G->etag.mode;
        
-#ifdef HTTP_HAVE_EXT_HASH
+#if defined(HTTP_HAVE_EXT_HASH) && !HTTP_SHARED_EXT(HASH)
        php_hash_ops *eho = NULL;
        
        if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) {
@@ -106,7 +108,7 @@ static inline char *_http_etag_finish(void *ctx TSRMLS_DC)
 static inline void _http_etag_update(void *ctx, const char *data_ptr, size_t data_len TSRMLS_DC)
 {
        char *mode = HTTP_G->etag.mode;
-#ifdef HTTP_HAVE_EXT_HASH
+#if defined(HTTP_HAVE_EXT_HASH) && !HTTP_SHARED_EXT(HASH)
        php_hash_ops *eho = NULL;
        
        if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) {
index ed869207429515a11d855f9b7d2e88794f5563cd..d5be364aa1d31101fcb263aa58dcb1e2bc785271 100644 (file)
@@ -15,7 +15,7 @@
 #ifndef PHP_HTTP_QUERYSTRING_API_H
 #define PHP_HTTP_QUERYSTRING_API_H
 
-#ifdef HAVE_ICONV
+#ifdef HAVE_ICONV && !HTTP_SHARED_EXT(ICONV)
 #define http_querystring_xlate(a, p, ie, oe) _http_querystring_xlate((a), (p), (ie), (oe) TSRMLS_CC)
 PHP_HTTP_API int _http_querystring_xlate(zval *array, zval *param, const char *ie, const char *oe TSRMLS_DC);
 #endif
index 0b77c3df482db54266829c7b834884c59e9cf074..4a60d12757b3dc58f9b0fcd64a42b461052a5914 100644 (file)
@@ -50,7 +50,7 @@ PHP_METHOD(HttpQueryString, getFloat);
 PHP_METHOD(HttpQueryString, getString);
 PHP_METHOD(HttpQueryString, getArray);
 PHP_METHOD(HttpQueryString, getObject);
-#ifdef HAVE_ICONV
+#ifdef HAVE_ICONV && !HTTP_SHARED_EXT(ICONV)
 PHP_METHOD(HttpQueryString, xlate);
 #endif
 #ifndef WONKY