I have a custom Views search_form_alter() Hook

function ubercart_prod_search_form_alter(&$form, $form_state, $form_id) {

    // Load up the $view objects to work with
    if (isset($form['#parameters'][1]['view'])) {
            $view = $form['#parameters'][1]['view'];
    }
    switch ($form_id) {
            case 'views_exposed_form':
            if ($view->name == 'searchproducts') {
                // Do range selector filter on 'sell_price'
                    ubercart_prod_search_range_to_select('sell_price', array(           // Views 'field name' = sell_price
                        '0,999' => 'Less than $1,000',
                        '1000,1999' => '$1,000 - $1,999',
                        '2000,3999' => '$2,000 - $3,999',
                        '4000,6999' => '$4,000 - $6,999',
                        '7000,999999' => '$7,000 or greater',
                    ), $form, $form_state);
                // Do range selector filter on 'art_height'
                    ubercart_prod_search_range_to_select('art_height', array(           // Views 'field name' = art_height
                        '0,499' => 'Less than 50cm high',
                        '500,999' => '50 - 99cm',
                        '1000,1999' => '100 - 199cm',
                        '2000,99999' => '200cm or greater',
                    ), $form, $form_state);
                // Do range selector filter on 'art_height'
                    ubercart_prod_search_range_to_select('art_width', array(            // Views 'field name' = art_width
                        '0,499' => 'Less than 50cm wide',
                        '500,999' => '50 - 99cm',
                        '1000,1999' => '100 - 199cm',
                        '2000,99999' => '200cm or greater',
                    ), $form, $form_state);
            $form['allterm_node_tid_depth']['#options']['All'] = t('All Artists');      // Change 'All' to 'All Artists'
//          $form['art_media']['#options']['All'] = t('All media types');               // Change 'All' to 'All Media'
            $form['art_height']['#options']['All'] = t('Any Height');                   // Change 'All' to 'All Heights'
            $form['art_width']['#options']['All'] = t('Any Width');                     // Change 'All' to 'All Widths'
            $form['sell_price']['#options']['All'] = t('All prices');                   // Change 'All' to 'All Prices'
            $form['submit']['#value'] = t('Search Art');                                // Change 'Submit' button to 'Search'
            }
            break;
    }
}

Showing only a part of the above...

Problem: the Views QUERY only returns results for numeric data, but not the '<Any>'.
If I over-ride the page URL for the Views query from say

URL/searchproducts?sell_price=All

URL/searchproducts?sell_price=0%2c1000

The query works fine, otherwise the result is blank.

More info, later, if you need.

Comments

Peter Bowey’s picture

The rest of the code:

/**
 * Turn a range field into a select dropdown. This assumes that the $options array
 * is going to be something like: array('5,9' => '5 to 9 lbs') where the index is
 * a comma delimited string of the min/max values.
 * Pass in $optional = TRUE if you want there to be an <any> value at the top of
 * the select dropdown. Defaults to TRUE.
 */
function ubercart_prod_search_range_to_select($field, $options, &$form, &$form_state, $optional = TRUE) {
    $form[$field]['#type'] = 'select';
    if ($optional) {
        $options = array_merge(array('All' => '<Any>'), $options);
    }
    $form[$field]['#options'] = $options;
    unset($form[$field]['min']);
    unset($form[$field]['max']);
    $f = $form_state['input'][$field];
    $f ? $form[$field]['#value'] = $f : true;
    $form[$field]['#element_validate'] = array('ubercart_prod_search_range_validate');
}

/**
 * Turn values created by range_to_select back into ranges so that Views can process
 * the request. This assumes that if the value passed in is 'All' the min/max array
 * should be set to array('min' => '', 'max' => '')
 */
function ubercart_prod_search_range_validate($element, &$form_state) {
  if (($v = $element['#post'][$element['#name']])) {
    if ($v == 'All') {
      $min = '';
      $max = '';
    }
    else {
      list($min, $max) = explode(',', $v);
    }
    $form_state['input'][$element['#name']] = array(
      'min' => $min,
      'max' => $max,
    );
    $form_state['values'][$element['#name']] = array(
      'min' => $min,
      'max' => $max,
    );
  }
}
Peter Bowey’s picture

Priority: Critical » Major

This is how the Query looks:

# User@Host: al_admin[al_admin] @ localhost []
# Query_time: 0.001329  Lock_time: 0.000095 Rows_sent: 0  Rows_examined: 1107
SET timestamp=1334679442;
SELECT node.nid AS nid,
   node.type AS node_type,
   node.vid AS node_vid,
   node.title AS node_title,
   uc_products.sell_price AS uc_products_sell_price,
   uc_products.height AS uc_products_height,
   uc_products.length_units AS uc_products_length_units,
   uc_products.width AS uc_products_width,
   uc_products.ordering AS uc_products_ordering,
   'searchproducts:default' AS view_name
 FROM node node
 LEFT JOIN uc_products uc_products ON node.vid = uc_products.vid
 WHERE (node.type IN ('product')) AND (node.type in ('product')) AND (uc_products.height >= 0) AND (uc_products.height <= 0) AND (uc_products.width >= 0) AND (uc_products.width <= 0) AND (uc_products.sell_price >= 0) AND (uc_products.sell_price <= 0)
   ORDER BY uc_products_ordering ASC
 LIMIT 0, 12;
Peter Bowey’s picture

Priority: Major » Critical

Manually running the above Query in MySQL also returns no results!

However, this modified MySQL Query works:

SELECT node.nid AS nid,
node.type AS node_type,
node.vid AS node_vid,
node.title AS node_title,
uc_products.sell_price AS uc_products_sell_price,
uc_products.height AS uc_products_height,
uc_products.length_units AS uc_products_length_units,
uc_products.width AS uc_products_width,
uc_products.ordering AS uc_products_ordering,
'searchproducts:default' AS view_name
FROM node node
LEFT JOIN uc_products uc_products ON node.vid = uc_products.vid
WHERE (node.type IN ('product')) AND (node.type in ('product')) AND (uc_products.height >= 0) AND (uc_products.height <= 10000) AND (uc_products.width >=0) AND (uc_products.width <= 100000) AND (uc_products.sell_price >= 0) AND (uc_products.sell_price <=10000)
ORDER BY uc_products_ordering ASC
LIMIT 0, 12;

So what do do to get Views to do that [range]?

Peter

Peter Bowey’s picture

Priority: Major » Critical

The above code worked fine with older MySQL + PHP

dawehner’s picture

Priority: Critical » Major

Support questions can't be critical , because, well they don't break views for thousands of sites.

As you say this is a bug which only exists on php 5.4, do you have any suggestion how to be able to reproduce this without your custom code? Your custom code for example could have this bug as well. I didn't saw something which is that obvious wrong and could cause the problem.

The best thing you could do, at least to get help by us, is to describe the problem in a way that everyone can just see this bug on a clean normal drupal installation. Installing php 5.4 wouldn't be a problem for me, though, but i currently use 5.3 just for convenience.

Peter Bowey’s picture

History / Background:

New Server: PHP 5.4.0 + MySQL 5.5.12 @ InnoDB
Old Server: PHP 5.2.17 +MySQL 5.0.51a @ MyISAM

Drupal [Pressflow] 6.25 + MySQL Database was migrated from an older Server -
with PHP 5.2.17 +MySQL 5.0.51a to New Server with PHP 5.4.0 + MySQL 5.5.12.

Notes: Prior to this all current D6 module updates applied.

The custom Views search_form_alter() Hook run correctly
on the old server; but on the new server the default <All>
returned no query results for numeric ranges . Works
fine on selected values [numeric].

With the Views search_form_alter(), Text ranges <All> is fine!

dawehner’s picture

Some other link you might want consider to provide more valuable information: http://drupal.org/node/571990

Peter Bowey’s picture

So I can debug this issue, in what part of 'Views' is the
page URL/searchproducts?sell_price=All translated to:

LEFT JOIN uc_products uc_products ON node.vid = uc_products.vid
WHERE (node.type IN ('product')) AND (node.type in ('product')) AND (uc_products.height >= 0) AND (uc_products.height <= 0) AND (uc_products.width >= 0) AND (uc_products.width <= 0) AND (uc_products.sell_price >= 0) AND (uc_products.sell_price <= 0)
Peter Bowey’s picture

Using grep, I suspect I need to start debug in one of the following Views files:

modules/taxonomy/views_handler_filter_term_node_tid_depth.inc
modules/taxonomy/views_plugin_argument_validate_taxonomy_term.inc

Peter Bowey’s picture

After a lot of debugging, I have determined that the issue lies with the
#element_validate which run's a function before the submission.

At this point, using the D6 views 'between' filter, the very first value [character]
of the ['max'] value is inserted - and over writes the first value [character]
of the ['min'] value. Effectively 'corrupting' the intended 'min' value of the 'between'
Views filter
. Only the very first character value of 'min' is effected - and it always uses
the very first character of the 'max' value - submitted.

At the moment, I cannot back-trace enough code to see what is causing this.

Peter Bowey’s picture

Priority: Major » Critical

** Issue Resolved **

This is a Views 6.x-2.x-dev PHP 5.4.x Bug.
Tested, back-traced, and proven!

Fix / Patch: [crude-but it works]
--------------------------------------

In the Views Tree: file => /handlers/views_handler_filter_numeric.inc

Change these lines:
      if ($which == 'all') {
        $dependency = array(
          '#process' => array('views_process_dependency'),
          '#dependency' => array($source => $this->operator_values(2)),
        );
        $form['value']['min'] += $dependency;
        $form['value']['max'] += $dependency;
      }
      if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['min'])) {
        $form_state['input'][$identifier]['min'] = $this->value['min'];
      }
      if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['max'])) {
        $form_state['input'][$identifier]['max'] = $this->value['max'];
      }
To this:

(basically, this 'change' checks that the item is an array - first).

      if ($which == 'all') {
        $dependency = array(
          '#process' => array('views_process_dependency'),
          '#dependency' => array($source => $this->operator_values(2)),
        );
        $form['value']['min'] += $dependency;
        $form['value']['max'] += $dependency;
      }
      if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['min'])) {
+        if(is_array($this->value['max'])) {
            $form_state['input'][$identifier]['min'] = $this->value['min'];
+        }
      }
      if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['max'])) {
+          if(is_array($this->value['max'])) {
          $form_state['input'][$identifier]['max'] = $this->value['max'];
+        }
      }

The actual PHP5.4.x warning:

Illegal string offset 'min' in views_handler_filter_numeric->value_form() (line 193 + 196 of /var/www/virtual/artlogic.com.au/sites/all/modules/views/handlers/views_handler_filter_numeric.inc).

I originally ignored this simply because it only maked as a PHP 'warning', but it turns out that while PHP 5.3.x would overlook if an item was not an array, PHP 5.4.x it now throws this warning [which becomes an code error] when it’s expecting an array (for 'min' in this case).

This is the patch that got things working for me. I know the developers will improve it.

Moving this thread to a Views 'Bug' status! Then possibly a patch (or a series of for PHP 5.4.x)

merlinofchaos’s picture

Title: Using Latest PHP 5.4 with MySQL 5.5.22 + D6 » Using Latest PHP 5.4 with MySQL 5.5.22 + D6: Illegal string offset 'min' *Resolved*
Category: support » bug
Issue tags: +Illegal string offset 'min'

ON what grounds is this critical?

Peter Bowey’s picture

Priority: Critical » Major

Well, PHP 5.4.1 is to be released on the 26th of April 2012.

Some Drupal users /innovators may already be moving to PHP 5.4.0.
(I have -though there are not many - yet)

Was marked as 'Critical' because Views will 'break code' under PHP 5.4.x (read 'error').

Peter Bowey’s picture

See http://drupal.org/node/1543434 for a full update

merlinofchaos’s picture

Does it whitescreen?

Peter Bowey’s picture

Refer #15

Quote: "Does it whitescreen?"

Answer:
Before the patch, no results are returned - on the above 'Views query'.
Not it does not 'whitescreen' in that sense, just no data on the return()

michaelfavia’s picture

I didnt expect to open a 3 year old case this morning but I had a deep dive with the same type of form alter in D7 and ran into the same issue with php 5.4 on views 3. His suggested fix (while applied to a different line and context) worked as well but not for any reason i understand yet. Im goign to throw it in a debugger and see what i can figure out but basically the key seemt o be in preventing the middle line in this if clause from executing. THe last is_array() is what he added but from what i can tell it could just as easily be FALSE because i dont see a condition in which it is an array.

/handlers/views_handler_filter_numeric.inc

      if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier]['min']) && is_array($this->value['max'])) {
        $form_state['input'][$identifier]['min'] = $this->value['min'];
      }

Once this line is removed from the process the select checkbox is correctly applied and automatically selected as the "last" value on filter page refreshes/submissions. If anyone can comment to the purpose of:

$form_state['input'][$identifier]['min'] = $this->value['min'];
chris matthews’s picture

Status: Active » Closed (outdated)

The Drupal 6 branch is no longer supported, please check with the D6LTS project if you need further support. For more information as to why this issue was closed, please see issue #3030347: Plan to clean process issue queue