Index: zend.c
===================================================================
RCS file: /repository/ZendEngine2/zend.c,v
retrieving revision 1.308.2.12.2.25
diff -u -r1.308.2.12.2.25 zend.c
--- zend.c	6 Dec 2006 09:52:50 -0000	1.308.2.12.2.25
+++ zend.c	11 Dec 2006 11:19:32 -0000
@@ -214,7 +214,7 @@
 			}
 			break;
 		case IS_RESOURCE:
-			expr_copy->value.str.val = (char *) emalloc(sizeof("Resource id #")-1 + MAX_LENGTH_OF_LONG);
+			expr_copy->value.str.val = (char *) emalloc(sizeof("Resource id #") + MAX_LENGTH_OF_LONG);
 			expr_copy->value.str.len = sprintf(expr_copy->value.str.val, "Resource id #%ld", expr->value.lval);
 			break;
 		case IS_ARRAY:
Index: zend_exceptions.c
===================================================================
RCS file: /repository/ZendEngine2/zend_exceptions.c,v
retrieving revision 1.79.2.6.2.5
diff -u -r1.79.2.6.2.5 zend_exceptions.c
--- zend_exceptions.c	21 Jul 2006 08:39:30 -0000	1.79.2.6.2.5
+++ zend_exceptions.c	11 Dec 2006 11:23:22 -0000
@@ -404,7 +404,7 @@
 		} else {
 			line = 0;
 		}
-		s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 2 + 1);
+		s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
 		sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
 		TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
 		efree(s_tmp);
Index: zend_language_scanner.l
===================================================================
RCS file: /repository/ZendEngine2/zend_language_scanner.l,v
retrieving revision 1.131.2.11.2.2
diff -u -r1.131.2.11.2.2 zend_language_scanner.l
--- zend_language_scanner.l	23 Nov 2006 22:04:54 -0000	1.131.2.11.2.2
+++ zend_language_scanner.l	18 Jan 2007 11:08:50 -0000
@@ -1236,38 +1236,43 @@
 
 
 <ST_IN_SCRIPTING>{LNUM} {
-	errno = 0;
-	zendlval->value.lval = strtol(yytext, NULL, 0);
-	if (errno == ERANGE) { /* overflow */
-		zendlval->value.dval = zend_strtod(yytext, NULL);
-		zendlval->type = IS_DOUBLE;
-		return T_DNUMBER;
+	if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */
+		zendlval->value.lval = strtol(yytext, NULL, 0);
 	} else {
-		zendlval->type = IS_LONG;
-		return T_LNUMBER;
-	}
-}
-
-<ST_IN_SCRIPTING>{HNUM} {
-	errno = 0;
-	zendlval->value.lval = strtoul(yytext, NULL, 16);
-	if (errno == ERANGE) { /* overflow */
-		/* not trying strtod - it returns trash on 0x-es */
-		zendlval->value.lval = LONG_MAX; /* maximal long */
-		zend_error(E_NOTICE,"Hex number is too big: %s", yytext);
-	} else {
-		if (zendlval->value.lval < 0) {
-			/* maintain consistency with the old way */
-			zendlval->value.dval = (unsigned long) zendlval->value.lval;
+		errno = 0;
+		zendlval->value.lval = strtol(yytext, NULL, 0);
+		if (errno == ERANGE) { /* Overflow */
+			zendlval->value.dval = zend_strtod(yytext, NULL);
 			zendlval->type = IS_DOUBLE;
 			return T_DNUMBER;
 		}
-		zendlval->type = IS_LONG;
 	}
+
 	zendlval->type = IS_LONG;
 	return T_LNUMBER;
 }
 
+<ST_IN_SCRIPTING>{HNUM} {
+	char *hex = yytext + 2; /* Skip "0x" */
+	int len = yyleng - 2;
+
+	/* Skip any leading 0s */
+	while (*hex == '0') {
+		hex++;
+		len--;
+	}
+
+	if (len < SIZEOF_LONG * 2 || (len == SIZEOF_LONG * 2 && *hex <= '7')) {
+		zendlval->value.lval = strtol(hex, NULL, 16);
+		zendlval->type = IS_LONG;
+		return T_LNUMBER;
+	} else {
+		zendlval->value.dval = zend_hex_strtod(hex, NULL);
+		zendlval->type = IS_DOUBLE;
+		return T_DNUMBER;
+	}
+}
+
 <ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>{LNUM}|{HNUM} { /* treat numbers (almost) as strings inside encapsulated strings */
 	zendlval->value.str.val = (char *)estrndup(yytext, yyleng);
 	zendlval->value.str.len = yyleng;
Index: zend_operators.c
===================================================================
RCS file: /repository/ZendEngine2/zend_operators.c,v
retrieving revision 1.208.2.4.2.15
diff -u -r1.208.2.4.2.15 zend_operators.c
--- zend_operators.c	11 Dec 2006 15:59:41 -0000	1.208.2.4.2.15
+++ zend_operators.c	19 Dec 2006 10:32:38 -0000
@@ -123,14 +123,9 @@
 				char *strval;
 
 				strval = op->value.str.val;
-				switch ((op->type=is_numeric_string(strval, op->value.str.len, &op->value.lval, &op->value.dval, 1))) {
-					case IS_DOUBLE:
-					case IS_LONG:
-						break;
-					default:
-						op->value.lval = strtol(op->value.str.val, NULL, 10);
-						op->type = IS_LONG;
-						break;
+				if ((op->type=is_numeric_string(strval, op->value.str.len, &op->value.lval, &op->value.dval, 1)) == 0) {
+					op->value.lval = 0;
+					op->type = IS_LONG;
 				}
 				STR_FREE(strval);
 				break;
@@ -161,14 +156,9 @@
 		switch ((op)->type) {										\
 			case IS_STRING:											\
 				{													\
-					switch (((holder).type=is_numeric_string((op)->value.str.val, (op)->value.str.len, &(holder).value.lval, &(holder).value.dval, 1))) {	\
-						case IS_DOUBLE:															\
-						case IS_LONG:															\
-							break;																\
-						default:																\
-							(holder).value.lval = strtol((op)->value.str.val, NULL, 10);		\
-							(holder).type = IS_LONG;						\
-							break;											\
+					if (((holder).type=is_numeric_string((op)->value.str.val, (op)->value.str.len, &(holder).value.lval, &(holder).value.dval, 1)) == 0) {	\
+						(holder).value.lval = 0;						\
+						(holder).type = IS_LONG;						\
 					}														\
 					(op) = &(holder);										\
 					break;													\
@@ -560,7 +550,7 @@
 			TSRMLS_FETCH();
 
 			zend_list_delete(op->value.lval);
-			op->value.str.val = (char *) emalloc(sizeof("Resource id #")-1 + MAX_LENGTH_OF_LONG);
+			op->value.str.val = (char *) emalloc(sizeof("Resource id #") + MAX_LENGTH_OF_LONG);
 			op->value.str.len = sprintf(op->value.str.val, "Resource id #%ld", tmp);
 			break;
 		}
@@ -1958,19 +1948,23 @@
 		(ret2=is_numeric_string(s2->value.str.val, s2->value.str.len, &lval2, &dval2, 0))) {
 		if ((ret1==IS_DOUBLE) || (ret2==IS_DOUBLE)) {
 			if (ret1!=IS_DOUBLE) {
-				dval1 = zend_strtod(s1->value.str.val, NULL);
+				dval1 = (double) lval1;
 			} else if (ret2!=IS_DOUBLE) {
-				dval2 = zend_strtod(s2->value.str.val, NULL);
+				dval2 = (double) lval2;
+			} else if (dval1 == dval2 && !zend_finite(dval1)) {
+				/* Both values overflowed and have the same sign,
+				 * so a numeric comparison would be inaccurate */
+				goto string_cmp;
 			}
 			result->value.dval = dval1 - dval2;
 			result->value.lval = ZEND_NORMALIZE_BOOL(result->value.dval);
 			result->type = IS_LONG;
 		} else { /* they both have to be long's */
-			result->value.lval = lval1 - lval2;
-			result->value.lval = ZEND_NORMALIZE_BOOL(result->value.lval);
+			result->value.lval = lval1 > lval2 ? 1 : (lval1 < lval2 ? -1 : 0);
 			result->type = IS_LONG;
 		}
 	} else {
+string_cmp:
 		result->value.lval = zend_binary_zval_strcmp(s1, s2);
 		result->value.lval = ZEND_NORMALIZE_BOOL(result->value.lval);
 		result->type = IS_LONG;
Index: zend_operators.h
===================================================================
RCS file: /repository/ZendEngine2/zend_operators.h,v
retrieving revision 1.94.2.4.2.6
diff -u -r1.94.2.4.2.6 zend_operators.h
--- zend_operators.h	11 Dec 2006 15:16:07 -0000	1.94.2.4.2.6
+++ zend_operators.h	15 Dec 2006 12:32:59 -0000
@@ -36,7 +36,16 @@
 #include "ext/bcmath/libbcmath/src/bcmath.h"
 #endif
 
+#if SIZEOF_LONG == 4
+#define MAX_LENGTH_OF_LONG 11
+static const char long_min_digits[] = "2147483648";
+#elif SIZEOF_LONG == 8
 #define MAX_LENGTH_OF_LONG 20
+static const char long_min_digits[] = "9223372036854775808";
+#else
+#error "Unknown SIZEOF_LONG"
+#endif
+
 #define MAX_LENGTH_OF_DOUBLE 32
 
 BEGIN_EXTERN_C()
@@ -66,82 +75,143 @@
 ZEND_API zend_bool instanceof_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC);
 END_EXTERN_C()
 
+#define ZEND_IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
+#define ZEND_IS_XDIGIT(c) (((c) >= 'A' && (c) <= 'F') || ((c) >= 'a'  && (c) <= 'f'))
+
 /**
- * Checks whether the string "str" with the length "length" is a numeric string.
+ * Checks whether the string "str" with length "length" is numeric. The value
+ * of allow_errors determines whether it's required to be entirely numeric, or
+ * just its prefix. Leading whitespace is allowed.
  *
- * The function returns 0 if the string did not contain a string; IS_LONG if
- * the string contained a number that fits in the integer range and IS_DOUBLE
- * in case it did not. The long value is returned into the pointer *lval if
- * that pointer was not NULL or into the pointer *dval if that pointer was not
- * NULL.
+ * The function returns 0 if the string did not contain a valid number; IS_LONG
+ * if it contained a number that fits within the range of a long; or IS_DOUBLE
+ * if the number was out of long range or contained a decimal point/exponent.
+ * The number's value is returned into the respective pointer, *lval or *dval,
+ * if that pointer is not NULL.
  */
-static inline zend_bool is_numeric_string(char *str, int length, long *lval, double *dval, int allow_errors)
+
+static inline zend_uchar is_numeric_string(const char *str, int length, long *lval, double *dval, int allow_errors)
 {
-	long local_lval;
+	const char *ptr;
+	int base = 10, digits = 0, dp_or_e = 0;
 	double local_dval;
-	char *end_ptr_long, *end_ptr_double;
-	int conv_base=10;
+	zend_uchar type;
 
 	if (!length) {
 		return 0;
 	}
 
-	/* handle hex numbers */
-	if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
-		conv_base=16;
-	}
-	errno=0;
-	local_lval = strtol(str, &end_ptr_long, conv_base);
-	if (errno!=ERANGE) {
-		if (end_ptr_long == str+length) { /* integer string */
-			if (lval) {
-				*lval = local_lval;
-			}
-			return IS_LONG;
-		} else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */
-			return 0;
+	/* Skip any whitespace
+	 * This is much faster than the isspace() function */
+	while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') {
+		str++;
+		length--;
+	}
+	ptr = str;
+
+	if (*ptr == '-' || *ptr == '+') {
+		ptr++;
+	}
+
+	if (ZEND_IS_DIGIT(*ptr)) {
+		/* Handle hex numbers
+		 * str is used instead of ptr to disallow signs and keep old behavior */
+		if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) {
+			base = 16;
+			ptr += 2;
 		}
-	} else {
-		end_ptr_long=NULL;
-	}
 
-	if (conv_base==16) { /* hex string, under UNIX strtod() messes it up */
-		return 0;
-	}
+		/* Skip any leading 0s */
+		while (*ptr == '0') {
+			ptr++;
+		}
 
-	errno=0;
-	local_dval = zend_strtod(str, &end_ptr_double);
-	if (errno != ERANGE) {
-		if (end_ptr_double == str+length) { /* floating point string */
-			if (!zend_finite(local_dval)) {
-				/* "inf","nan" and maybe other weird ones */
-				return 0;
+		/* Count the number of digits. If a decimal point/exponent is found,
+		 * it's a double. Otherwise, if there's a dval or no need to check for
+		 * a full match, stop when there are too many digits for a long */
+		for (type = IS_LONG; !(digits >= MAX_LENGTH_OF_LONG && (dval || allow_errors == 1)); digits++, ptr++) {
+check_digits:
+			if (ZEND_IS_DIGIT(*ptr) || (base == 16 && ZEND_IS_XDIGIT(*ptr))) {
+				continue;
+			} else if (base == 10) {
+				if (*ptr == '.' && dp_or_e < 1) {
+					goto process_double;
+				} else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) {
+					const char *e = ptr + 1;
+
+					if (*e == '-' || *e == '+') {
+						ptr = e++;
+					}
+					if (ZEND_IS_DIGIT(*e)) {
+						goto process_double;
+					}
+				}
 			}
 
+			break;
+		}
+
+		if (base == 10) {
+			if (digits >= MAX_LENGTH_OF_LONG) {
+				dp_or_e = -1;
+				goto process_double;
+			}
+		} else if (!(digits < SIZEOF_LONG * 2 || (digits == SIZEOF_LONG * 2 && ptr[-digits] <= '7'))) {
 			if (dval) {
-				*dval = local_dval;
+				local_dval = zend_hex_strtod(str, (char **)&ptr);
 			}
-			return IS_DOUBLE;
+			type = IS_DOUBLE;
+		}
+	} else if (*ptr == '.' && ZEND_IS_DIGIT(ptr[1])) {
+process_double:
+		type = IS_DOUBLE;
+
+		/* If there's a dval, do the conversion; else continue checking
+		 * the digits if we need to check for a full match */
+		if (dval) {
+			local_dval = zend_strtod(str, (char **)&ptr);
+		} else if (allow_errors != 1 && dp_or_e != -1) {
+			dp_or_e = (*ptr++ == '.') ? 1 : 2;
+			goto check_digits;
 		}
 	} else {
-		end_ptr_double=NULL;
-	}
-
-	if (!allow_errors) {
 		return 0;
 	}
-	if (allow_errors == -1) {
-		zend_error(E_NOTICE, "A non well formed numeric value encountered");
+
+	if (ptr != str + length) {
+		if (!allow_errors) {
+			return 0;
+		}
+		if (allow_errors == -1) {
+			zend_error(E_NOTICE, "A non well formed numeric value encountered");
+		}
 	}
 
-	if (end_ptr_double>end_ptr_long && dval) {
-		*dval = local_dval;
-		return IS_DOUBLE;
-	} else if (end_ptr_long && lval) {
-		*lval = local_lval;
+	if (type == IS_LONG) {
+		if (digits == MAX_LENGTH_OF_LONG - 1) {
+			int cmp = strcmp(&ptr[-digits], long_min_digits);
+
+			if (!(cmp < 0 || (cmp == 0 && *str == '-'))) {
+				if (dval) {
+					*dval = zend_strtod(str, NULL);
+				}
+
+				return IS_DOUBLE;
+			}
+		}
+
+		if (lval) {
+			*lval = strtol(str, NULL, base);
+		}
+
 		return IS_LONG;
+	} else {
+		if (dval) {
+			*dval = local_dval;
+		}
+
+		return IS_DOUBLE;
 	}
-	return 0;
 }
 
 static inline char *
Index: zend_strtod.c
===================================================================
RCS file: /repository/ZendEngine2/zend_strtod.c,v
retrieving revision 1.17.2.2.2.3
diff -u -r1.17.2.2.2.3 zend_strtod.c
--- zend_strtod.c	7 Dec 2006 20:45:58 -0000	1.17.2.2.2.3
+++ zend_strtod.c	11 Dec 2006 11:06:10 -0000
@@ -2557,6 +2557,39 @@
 	return result;
 }
 
+ZEND_API double zend_hex_strtod(const char *str, char **endptr)
+{
+	const char *s = str;
+	char c;
+	int any = 0;
+	double value = 0;
+
+	if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) {
+		s += 2;
+	}
+
+	while ((c = *s++)) {
+		if (c >= '0' && c <= '9') {
+			c -= '0';
+		} else if (c >= 'A' && c <= 'F') {
+			c -= 'A' - 10;
+		} else if (c >= 'a' && c <= 'f') {
+			c -= 'a' - 10;
+		} else {
+			break;
+		}
+
+		any = 1;
+		value = value * 16 + c;
+	}
+
+	if (endptr != NULL) {
+		*endptr = (char *)(any ? s - 1 : str);
+	}
+
+	return value;
+}
+
 /*
  * Local variables:
  * tab-width: 4
Index: zend_strtod.h
===================================================================
RCS file: /repository/ZendEngine2/zend_strtod.h,v
retrieving revision 1.3.2.1.2.1
diff -u -r1.3.2.1.2.1 zend_strtod.h
--- zend_strtod.h	6 Dec 2006 09:52:50 -0000	1.3.2.1.2.1
+++ zend_strtod.h	11 Dec 2006 11:05:10 -0000
@@ -28,6 +28,7 @@
 ZEND_API void zend_freedtoa(char *s);
 ZEND_API char * zend_dtoa(double _d, int mode, int ndigits, int *decpt, int *sign, char **rve);
 ZEND_API double zend_strtod(const char *s00, char **se);
+ZEND_API double zend_hex_strtod(const char *str, char **endptr);
 ZEND_API int zend_startup_strtod(void);
 ZEND_API int zend_shutdown_strtod(void);
 END_EXTERN_C()
Index: ext/pcre/php_pcre.c
===================================================================
RCS file: /repository/php-src/ext/pcre/php_pcre.c,v
retrieving revision 1.168.2.9.2.12
diff -u -r1.168.2.9.2.12 php_pcre.c
--- ext/pcre/php_pcre.c	10 Oct 2006 12:44:02 -0000	1.168.2.9.2.12
+++ ext/pcre/php_pcre.c	11 Dec 2006 11:29:59 -0000
@@ -559,8 +559,7 @@
 		}
 		if (name_cnt > 0) {
 			int rc1, rc2;
-			long dummy_l;
-			double dummy_d;
+
 			rc1 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMETABLE, &name_table);
 			rc2 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMEENTRYSIZE, &name_size);
 			rc = rc2 ? rc2 : rc1;
@@ -574,7 +573,7 @@
 			while (ni++ < name_cnt) {
 				name_idx = 0xff * name_table[0] + name_table[1];
 				subpat_names[name_idx] = name_table + 2;
-				if (is_numeric_string(subpat_names[name_idx], strlen(subpat_names[name_idx]), &dummy_l, &dummy_d, 0) > 0) {
+				if (is_numeric_string(subpat_names[name_idx], strlen(subpat_names[name_idx]), NULL, NULL, 0) > 0) {
 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Numeric named subpatterns are not allowed");
 					efree(offsets);
 					efree(subpat_names);
Index: ext/standard/type.c
===================================================================
RCS file: /repository/php-src/ext/standard/type.c,v
retrieving revision 1.30.2.2
diff -u -r1.30.2.2 type.c
--- ext/standard/type.c	1 Jan 2006 12:50:15 -0000	1.30.2.2
+++ ext/standard/type.c	11 Dec 2006 11:27:29 -0000
@@ -305,7 +305,6 @@
 PHP_FUNCTION(is_numeric)
 {
 	zval **arg;
-	int result;
 
 	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
 		WRONG_PARAM_COUNT;
@@ -318,8 +317,7 @@
 			break;
 
 		case IS_STRING:
-			result = is_numeric_string(Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), NULL, NULL, 0);
-			if (result == IS_LONG || result == IS_DOUBLE) {
+			if (is_numeric_string(Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), NULL, NULL, 0)) {
 				RETURN_TRUE;
 			} else {
 				RETURN_FALSE;
