Hm, you can't pass alt in $variables['attributes']. (Same for src but for different reasons.) It always ends up as "".

It's a bit of a DrupalWTF, because title, width and height *can* be passed this way. The current code fails in picking up alt because of the isset() check on $variables['alt'] and the fact that $variables['alt'] is assigned '' as default in drupal_common_theme().

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

derjochenmeyer’s picture

Doc: theme_image($variables)

Why do you want to pass alt in $variables['attributes']?

$variables = array(
  'path' => 'path/to/img.jpg', 
  'alt' => 'Test alt',
  'title' => 'Test title',
  'width' => '50%',
  'height' => '50%',
  'attributes' => array('class' => 'some-img', 'id' => 'my-img'),
);
$img = theme('image', $variables);
Dave Reid’s picture

Priority: Major » Normal

Definitely not major.

derjochenmeyer’s picture

Status: Active » Closed (works as designed)

I think theme_image() works as designed. This is not a bug. Re-open if I'm wrong.

RobLoach’s picture

effulgentsia’s picture

Version: 7.x-dev » 8.x-dev
Status: Closed (works as designed) » Active
Issue tags: +Accessibility, +html5

I think this is a valid WTF that's worth fixing in D8. There are good use-cases for wanting to pass all HTML attributes in $variables['attributes'], especially in the context of render arrays. Given D8's default html output will be html5, it may make sense to default 'alt' to NULL like the rest of them, or else remove those 4 keys from being special at all, and simply use 'attributes' exclusively.

For D7, I think we're stuck with this, because of the need for legacy compatibility, and coddling people towards valid html4/xhtml. See #555830: Clean up theme_image() to use drupal_attributes() for all attributes and revisit defaults for "alt" and "title" for background.

sun’s picture

1) For D7, we want to replace the isset() with array_key_exists() to allow overriding the default of '' with NULL.

2) $variables vs. $variables['attributes'], we can fix, too.

For D8, we probably want to open a separate issue to remove the totally whack separated variables and only support 'attributes'. Or similar.

sun’s picture

Status: Active » Needs review
effulgentsia’s picture

Status: Needs review » Needs work
+++ b/includes/theme.inc
@@ -1512,9 +1512,17 @@ function theme_image($variables) {
+    if (array_key_exists($key, $variables)) {

This will always be TRUE, given the default in drupal_common_theme().

Everett Zufelt’s picture

Given D8's default html output will be html5, it may make sense to default 'alt' to NULL like the rest of them, or else remove those 4 keys from being special at all, and simply use 'attributes' exclusively.

I would be a fan of using just attributes, although, using explicit variables is useful so that people can see the commonly used attributes.

I would be a huge fan of alt * not * defaulting to ''. If alt doesn't default to '' then the html5 validator will most likely throw a warning (unless it is one of the stupid exclusions the co-chairs have added to the spec). The warning was intentionally designed so that people would be made aware of the missing alt attribute on the image, but still allows the page to validate.

** note: this is from memory of the spec and associated discussions and things may have changed.

mgifford’s picture

Status: Needs work » Needs review
Issue tags: -DrupalWTF, -Accessibility, -html5, -Needs backport to D7

Status: Needs review » Needs work
Issue tags: +DrupalWTF, +Accessibility, +html5, +Needs backport to D7

The last submitted patch, drupal8.theme-image-attributes.6.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review
FileSize
873 bytes

This is basically a re-roll. I didn't address @effulgentsia's concern in #8. Sounds like it should be just simplified and that if statement removed to simplify it.

mgifford’s picture

Status: Needs review » Needs work
Issue tags: +DrupalWTF, +Accessibility, +html5, +Needs backport to D7

The last submitted patch, drupal8.theme-image-attributes.999338-12.patch, failed testing.

mgifford’s picture

Priority: Normal » Major

I'm bumping this up to major because it's inconsistent and because it restricts accessibility for no good reason. This can be fixed in D8.

marcingy’s picture

Priority: Major » Normal

This is not major as per #2

Eric_A’s picture

Hm, if you pass in a NULL value for $variables['alt'] and nothing for $variables['attributes']['alt'], then the image element won't have an alt attribute. So there *is* a way, even if not very pretty. If we document this, then we have our official no alt API for D7 (and D8).

Returning to my original report: by passing in a NULL value for $variables['alt'], any value for $variables['attributes']['alt'] does win.

Regarding #8: that seems bogus. We have process, preprocess and registry_alter as possible ways to unset the alt key.

mgifford’s picture

Issue tags: +a11ySprint

Remember, alt tags are the low hanging fruit of accessibility. That being said, this should be easier.

mgifford’s picture

Issue tags: +Needs tests
mgifford’s picture

Status: Needs work » Needs review
mgifford’s picture

This simplifies it based on @effulgentsia - but I do agree with Everett that defaulting to alt="" will make it harder to find broken alt text. That should be a conscious choice made every time.

Status: Needs review » Needs work
Issue tags: -Needs tests, -DrupalWTF, -Accessibility, -html5, -Needs backport to D7, -a11ySprint

The last submitted patch, drupal8.theme-image-attributes.999338-21.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review
mgifford’s picture

mgifford’s picture

mgifford’s picture

RobLoach’s picture

Not sure about removing attributes if they arn't width, height, alt, or title. What if we want a data- attribute on an image?

mgifford’s picture

If we want to add data then it would have to be added to this array first - array('width', 'height', 'alt', 'title')

This only unsets the variables defined with the array above.

shanly’s picture

mgifford’s picture

mgifford’s picture

Status: Needs review » Needs work

The last submitted patch, drupal8.theme-image-attributes.999338-21.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests, +DrupalWTF, +Accessibility, +html5, +Needs backport to D7, +a11ySprint
mgifford’s picture

This is a very simple patch. Also the test results were filled up with things like : mkdir(): No space left on device

Status: Needs review » Needs work
Issue tags: -Needs tests, -DrupalWTF, -Accessibility, -html5, -Needs backport to D7, -a11ySprint

The last submitted patch, drupal8.theme-image-attributes.999338-21.patch, failed testing.

mgifford’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests, +DrupalWTF, +Accessibility, +html5, +Needs backport to D7, +a11ySprint
mgifford’s picture

This failed last time with:

Link with label Test Operation: ;::]A$7A found in EntityOperationsTest.php Line 54

Not sure hwo this would affect it:
$this->assertLinkByHref($uri['path'] . '/test_operation');

bowersox’s picture

Issue tags: +TwinCities

The tests look like they pass again now on the patch in 21.

mgifford’s picture

mgifford’s picture

Status: Needs review » Needs work

The last submitted patch, drupal8.theme-image-attributes.999338-21.patch, failed testing.

mgifford’s picture

Issue summary: View changes
Issue tags: +Novice
alarcombe’s picture

Fixed patch to reference core/includes/theme.inc rather than includes/theme.inc

alarcombe’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 44: theme_image_alt_attribute-999338-44.patch, failed testing.

alarcombe’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 44: theme_image_alt_attribute-999338-44.patch, failed testing.

mgifford’s picture

reroll.

alarcombe’s picture

Status: Needs work » Needs review

Let's get this slippery customer tested again before it needs another reroll!

Status: Needs review » Needs work

The last submitted patch, 49: theme_image_alt_attribute-999338-49.patch, failed testing.

lauriii’s picture

Assigned: Unassigned » lauriii
lauriii’s picture

Assigned: lauriii » Unassigned
Status: Needs work » Needs review
FileSize
1007 bytes
mgifford’s picture

We need a clear test to manually test this.

katiebot’s picture

Assigned: Unassigned » katiebot

We're looking into this at DrupalCon Austin.

mlncn’s picture

Assigned: katiebot » mlncn

Working with Katie, verified the problem. This module provides working and non-working (in attribute array) examples: https://drupal.org/sandbox/mlncn/2281847

The first render array displays its alt text, the second does not:

    $build['image_with_alt_working'] = array(
      '#theme' => 'image',
      '#uri' => '/core/themes/bartik/logo.png',
      '#alt' => 'Test alt',
      '#title' => 'Test title',
      '#width' => '50%',
      '#height' => '50%',
      '#attributes' => array('class' => 'some-img', 'id' => 'my-img'),
    );
    $build['image_with_alt_attribute_not_working'] = array(
      '#theme' => 'image',
      '#uri' => '/core/themes/bartik/logo.png',
      '#width' => '50%',
      '#height' => '50%',
      '#attributes' => array(
        'class' => 'some-img',
        'id' => 'my-img',
        'title' => 'New test title',
        'alt' => 'New test alt',
      ),
mlncn’s picture

Status: Needs review » Needs work

The attached patch from comment #53 does not change anything (a nice self-contained argument that this issue needs tests).

In Drupal 8, instead of making alt a special case which is pre-set to empty string because it is required, we should pre-set it to NULL like title, uri, width, and height. We no longer use an XHTML 1.0 or HTML 4 doctype and there is no need to complicate our code attempting to adhere to the letter, not the spirit, of accessibility to conform to its entirely unenforced .

This comment appears twice in our code and can be removed entirely along with its ensuing complication.

// HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft
// allows the alt attribute to be omitted in some cases. Therefore,
// default the alt attribute to an empty string, but allow code calling
// _theme('image') to pass explicit NULL for it to be omitted. Usually,
// neither omission nor an empty string satisfies accessibility
// requirements, so it is strongly encouraged for code calling
// _theme('image') to pass a meaningful value for the alt variable.
// - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
// - http://www.w3.org/TR/xhtml1/dtds.html
// - http://dev.w3.org/html5/spec/Overview.html#alt

The html5 specification (that last link) properly makes no distinction between a missing and an empty alt text:

"Except where otherwise specified, the alt attribute must be specified and its value must not be empty".

Even for xhtml 1.0, do any validators people use make this distinction? Could we get rid of this in 7 also? If not, i'm happy to make two different patches, one that's almost all removing silliness for 8, and a workaround of it for 7. But dropping the pre-set to empty string is what ezufelt calls for in comment #9.

mlncn’s picture

This patch fixes the problem that alt cannot be passed in with $variables['attributes'] by removing the misguided attempt to set it to "" if it isn't present. Test forthcoming.

mlncn’s picture

Assigned: mlncn » Unassigned
Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 58: theme_image_alt_attribute-999338-58.patch, failed testing.

mgifford’s picture

@mlncn I don't see how the patch in #10 resolves this problem.

HTML5 specs & WCAG 2.0 AA are different things. Our goal as a community is both, but for accessibility it is the latter.

The problem isn't as much of it coming up with alt="" as much as it not coming up with alt="New test alt" - to use your earlier example.

Good catch on the duplicate help text. There should be some reference though as it's 1000 lines of code after.

Sir-Arturio’s picture

Status: Needs work » Needs review
FileSize
2.69 KB

Hi.
I followed the basic Test Driven Development method to solve the problem.

First, I created an automated test based on @mlncn's #56 report/sandbox, which is included in my patch.

Then I created the minimal ammount of code that makes the test to pass. At least the tests in Drupal\image\Tests\ImageThemeFunctionTest do not break. Let's see what Drupal TestBot thinks...

So, my sollution solves the issue but does not take part in the wider discussion about should the shortcut variable alt default to NULL instead of ''.

Sir-Arturio’s picture

Issue tags: -Needs tests

Removing "Needs tests" tag as the automated tests have been implemented.

lauriii’s picture

Status: Needs review » Needs work

Patch looks good!

We still need to implement a separated test patch which proves us that the tests are working. More information about Automated tests.

Sir-Arturio’s picture

Here you are. I also refactored the tests as well.

The last submitted patch, 65: theme_image_alt_attribute-999338-65-tests.patch, failed testing.

lauriii’s picture

I updated the commentings a bit.

mgifford’s picture

@lauriii - Did you also "implement a separated test patch which proves us that the tests are working"?

Just wondering how close we're getting to RTBC and what other steps are required. Nice to see the bots like your patch.

lauriii’s picture

@mgifford - I didn't create separated test patch because there's one available in comment #65. Do you think it's enough to prove the tests are working?

mgifford’s picture

Issue tags: +TCDrupal 2014

I've got a test environment up here - http://s22557ad640c574b.s2.simplytest.me/

Although that's not all that useful in testing this.. I'll try to get some feedback on what would be required to prove the tests are working...

meichr’s picture

Assigned: Unassigned » meichr
Issue tags: +FUDK

I'm going to test this

meichr’s picture

Assigned: meichr » Unassigned
Status: Needs review » Reviewed & tested by the community
FileSize
55.61 KB
55.87 KB

By using the sandbox module referenced in comment #56 the error can be reproduced, while the first image has the correct ALT text applied, the second image has an empty ALT text:

The patch from comment #68 corrects the problem as now also the second image has the correct ALT text applied:

joelpittet’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
2.62 KB
2.82 KB

Doc 80 character fix and micro optimization:
@see http://www.zomeoff.com/php-fast-way-to-determine-a-key-elements-existanc...

Also moved it into the existing IF for further optimization so it doesn't need to check the NULL array key if there is no value to put in there in the first place.

Summary: isset() is super fast! Like 65 times faster than array_key_exists(). So do that isset() check first.

joelpittet’s picture

FileSize
2.62 KB
1 KB

Typo, that patch is not right.

meichr’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
60.16 KB

Confirming like in #73 that patch at #75 also corrects the problem, the second image also has the correct ALT text applied:

webchick’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/image/src/Tests/ImageThemeFunctionTest.php
    @@ -152,4 +152,41 @@ function testImageStyleTheme() {
    +    // Test using alt directly with alt attribute.
    +    $image_with_alt_working = array(
    ...
    +    $elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', array(":class" => "image-with-regular-alt", ":alt" => "Regular alt"));
    +    $this->assertEqual(count($elements), 1, 'Regular alt displays correctly');
    ...
    +    // Test using alt attribute inside attributes.
    +    $image_with_alt_attribute_not_working = array(
    ...
    +    $elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', array(":class" => "image-with-attribute-alt", ":alt" => "Attribute alt"));
    +    $this->assertEqual(count($elements), 1, 'Attribute alt displays correctly');
    

    This is pretty minor, but did confuse me in reading the test. $image_with_alt_attribute_not_working then later has an assertion that says the alt attribute actually IS working? I think we mean something there like:

    $image_with_alt_property

    and

    $image_with_alt_attribute

    Yes?

  2. +++ b/core/includes/theme.inc
    @@ -1125,6 +1125,11 @@ function template_preprocess_image(&$variables) {
    +      // If the property has already been defined in the attributes,
    +      // do not override, including NULL.
    +      if (isset($variables['attributes'][$key]) || array_key_exists($key, $variables['attributes'])) {
    +        continue;
    +      }
    

    And then additionally, I don't actually see tests for this condition.

lauriii’s picture

Status: Needs work » Needs review
FileSize
2.53 KB
2.8 KB
3.43 KB

Fixed variable names and added test for #77.2

The last submitted patch, 78: theme_image_alt-999338-78-tests.patch, failed testing.

joelpittet’s picture

Status: Needs review » Reviewed & tested by the community

Awesome! Thanks for test only patch!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/includes/theme.inc
@@ -1126,6 +1126,11 @@ function template_preprocess_image(&$variables) {
+      if (isset($variables['attributes'][$key]) || array_key_exists($key, $variables['attributes'])) {

This looks like an unnecessary micro-optimisation we'd have to be doing thousands of these per request to notice the difference. Why not just if (array_key_exists($key, $variables['attributes'])) {

<?php
class NonExistant
{
  protected $associativeArray = array(
    'one' => 'one',
    'two' => 'two',
    'three' => 'three',
    'four' => 'four',
    'five' => 'five',
    'six' => 'six',
  );

  protected $numIterations = 1000;
  
  public function issetTest()
  {
    for ($i = 0; $i < $this->numIterations; $i++) {
      isset($this->associativeArray['none']);
    }
  }

  public function arrayKeyExistsTest()
  {
    for ($i = 0; $i < $this->numIterations; $i++) {
      array_key_exists('none', $this->associativeArray);
    }
  }
}

$class = new NonExistant();

foreach (array('issetTest', 'arrayKeyExistsTest') as $func) {
  $time_start = microtime();
  $class->$func();
  $time_end = microtime();
  $time = $time_end - $time_start;

  echo "Did $func in $time seconds\n";
}

Outputs:

Did issetTest in 0.00011900000000004 seconds
Did arrayKeyExistsTest in 0.00024799999999997 seconds

So it is twice as slow as something that is really really fast - so it is still fast. Also see http://3v4l.org/hPpbU for comparison accross PHP versions.

lauriii’s picture

Status: Needs work » Needs review
FileSize
3.39 KB
544 bytes

I removed that unnecessary isset.

joelpittet’s picture

@alexpott regarding the micro-optimization. With a preprococess that gets called as much as this does, it is worth it.
see #74 for why I put it in. Also this optimization is in core in other places and I think @jessebeach walked me through it and convinced me it was the better way to IIRC.
From the article:

array_key_exists() is almost 5 times slower than isset()

joelpittet’s picture

Status: Needs review » Reviewed & tested by the community

re #81 I must have not seen those stats or you added them after, I did similar tests here and kinda came to the same conclusion, it's minimal.

The only thing that the isset() || array_key_exists() really does it make you ask your self as a developer, do you really need to have NULL keys because isset() does the exact same thing except it is FALSE on NULL.

I like the pattern because it highlights that decision.
Anyways, this is RTBC. Grievances aired, optimization minimal.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 6d8e333 and pushed to 8.0.x. Thanks!

  • alexpott committed 6d8e333 on 8.0.x
    Issue #999338 by lauriii, joelpittet, Sir-Arturio, mgifford, mlncn,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.