0

I'm creating a metabox to upload PDF files.

I have the following warning notice for this line of code: $this_file = $filearray['url'];

Warning: Illegal string offset ‘url’ in...

The complete function code:

function wp_custom_attachment() {
    wp_nonce_field( plugin_basename(__FILE__), 'wp_custom_attachment_nonce' );
    $html = '<p class="description">Upload your PDF here.</p>';
    $html .= '<input id="wp_custom_attachment" name="wp_custom_attachment" size="25" type="file" value="" />';

    $filearray = get_post_meta( get_the_ID(), 'wp_custom_attachment', true );
    $this_file = $filearray['url'];
    
    if ( $this_file != '' ) { 
         $html .= '<div><p>Current file: ' . $this_file . '</p></div>'; 
    }
    echo $html; 
}

Thanks in advance for the help.

EDIT

function add_custom_meta_boxes() {
    add_meta_box( 
        'wp_custom_attachment',
        'PDF File',
        'wp_custom_attachment',
        'workshop',
        'normal'
    ) ;
}
add_action( 'add_meta_boxes', 'add_custom_meta_boxes' );

function wp_custom_attachment() {
    wp_nonce_field( plugin_basename(__FILE__), 'wp_custom_attachment_nonce' );
    $html = '<p class="description">Upload your PDF here.</p>';
    $html .= '<input id="wp_custom_attachment" name="wp_custom_attachment" size="25" type="file" value="" />';

    $filearray = get_post_meta( get_the_ID(), 'wp_custom_attachment', true );
    $this_file = $filearray['url'];
    
    if ( $this_file != '' ) { 
         $html .= '<div><p>Current file: ' . $this_file . '</p></div>'; 
    }
    echo $html; 
}

function save_custom_meta_data( $id ) {
    if ( ! empty( $_FILES['wp_custom_attachment']['name'] ) ) {
        $supported_types = array( 'application/pdf' );
        $arr_file_type = wp_check_filetype( basename( $_FILES['wp_custom_attachment']['name'] ) );
        $uploaded_type = $arr_file_type['type'];

        if ( in_array( $uploaded_type, $supported_types ) ) {
            $upload = wp_upload_bits($_FILES['wp_custom_attachment']['name'], null, file_get_contents($_FILES['wp_custom_attachment']['tmp_name']));
            if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
                wp_die( 'There was an error uploading your file. The error is: ' . $upload['error'] );
            } else {
                add_post_meta( $id, 'wp_custom_attachment', $upload );
                update_post_meta( $id, 'wp_custom_attachment', $upload );
            }
        }
        else {
            wp_die( "The file type that you've uploaded is not a PDF." );
        }
    }
}
add_action( 'save_post', 'save_custom_meta_data' );

function update_edit_form() {
    echo ' enctype="multipart/form-data"';
}
add_action( 'post_edit_form_tag', 'update_edit_form' );
6
  • Where is wp_custom_attachment() used? Is the current post always going to have a wp_custom_attachment field saved? Your code doesn't handle the possibility that there's no value. And if there is a value, what does it look like? Commented Jul 1, 2021 at 15:43
  • @JacobPeattie I edited the question by providing the full code. Commented Jul 1, 2021 at 15:53
  • 1
    Ok, so think this through. When you create a new post, that post is not going to have this file, right? So how can ['url'] inside $filearray exist? Commented Jul 1, 2021 at 15:56
  • I understand, it's logic. However I'm struggling to resolve the issue. I'm starting in PHP so I block easily on this kind of problem. Commented Jul 1, 2021 at 16:33
  • I think I found a solution. Let me know if I'm wrong or right: if ( isset( $filearray ) && '' !== $filearray ) : $this_file = $filearray['url']; endif; Commented Jul 1, 2021 at 16:53

1 Answer 1

2

You need to be validating a couple of parts about $filearray before you use it.

There are some good suggestions in the comments, I think a couple of robust examples would serve you well too:

Using empty()

function wp_custom_attachment() {
    wp_nonce_field( plugin_basename(__FILE__), 'wp_custom_attachment_nonce' );
    $html = '<p class="description">Upload your PDF here.</p>';
    $html .= '<input id="wp_custom_attachment" name="wp_custom_attachment" size="25" type="file" value="" />';

    $filearray = get_post_meta( get_the_ID(), 'wp_custom_attachment', true );
    
    if ( ! empty( $filearray['url'] ) ) { 
         $html .= '<div><p>Current file: ' . $filearray['url'] . '</p></div>'; 
    }
    echo $html; 
}

empty() is great because it implicitly works like isset, whereby if the value isn't set it just returns false. Another benefit of empty is that you can test for structures like array keys without worrying about whether the object is an array at all:

php > var_dump( empty( $a['foo'] ) ); // $a is undefined
php shell code:1:
bool(true)
php > var_dump( empty( $a['foo']['bar'][32] ) ); // $a is undefined
php shell code:1:
bool(true)
php > var_dump( empty( $o->foo ) ); // $o is undefined
php shell code:1:
bool(true)

Above, you can see that PHP isn't complaining about the lack of $a or $o being undefined, it just tells us "yes that is empty" and carries on.

Using isset and is_array

function wp_custom_attachment() {
    wp_nonce_field( plugin_basename(__FILE__), 'wp_custom_attachment_nonce' );
    $html = '<p class="description">Upload your PDF here.</p>';
    $html .= '<input id="wp_custom_attachment" name="wp_custom_attachment" size="25" type="file" value="" />';

    $filearray = get_post_meta( get_the_ID(), 'wp_custom_attachment', true );

    if ( is_array( $filearray) && isset( $filearray['url'] ) ) { 
         $html .= '<div><p>Current file: ' . $filearray['url'] . '</p></div>'; 
    }
    echo $html; 
}

This approach does two parts of validation - first, we ensure that $filearray is indeed an array, and then we validate that the url key exists. This is more verbose than empty, and has the added benefit of a type check. It should be noted that empty( $filearray['url'] ) will only work if $filearray is an array because the syntax dictates it, however, this check is not explicit.

A note on ternary and null-coalesce

Some people suggested using a ternary:

$this_file = ! empty( $filearray['url'] ) ? $filearray['url'] : '';

This is fine, though it's a bit hard to grok at first. You could also use null-coalescing if your PHP version supports it:

$this_file = $filearray['url'] ?? '';

While these work fine, again, I'd avoid them until you're more comfortable in PHP. It stinks to come back to old code a few months later to try and remember what the heck you did when your code looks like

$foo = $bar ? ( $baz ?? false ) : ( $done ?: 'Waiting' );
1
  • Thank you so much @phatskat for all the explanation and examples. Commented Jul 3, 2021 at 6:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.