Whoops \ Exception \ ErrorException (E_WARNING)
Attempt to read property "post_title" on null Whoops\Exception\ErrorException thrown with message "Attempt to read property "post_title" on null" Stacktrace: #14 Whoops\Exception\ErrorException in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Content/Image.php:50 #13 Whoops\Run:handleError in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Content/Image.php:50 #12 THEME\ViewModel\Post\Content\Image:getImageData in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Content/Image.php:41 #11 THEME\ViewModel\Post\Content\Image:__construct in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/ContentSplitters/Image.php:66 #10 THEME\ViewModel\Post\Components\ContentSplitters\Image:getImage in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/ContentSplitters/Image.php:51 #9 THEME\ViewModel\Post\Components\ContentSplitters\Image:split in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php:110 #8 THEME\ViewModel\Post\Components\SplittedContent:splitSections in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php:61 #7 THEME\ViewModel\Post\Components\SplittedContent:init in /data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php:23 #6 THEME\ViewModel\Post\Components\SplittedContent:__construct in /data/websites/motori/web/app/themes/motori/src/ViewModel/Common/HasComponents.php:15 #5 THEME\ViewModel\Base:loadComponents in /data/websites/motori/web/app/themes/motori/src/ViewModel/Base.php:34 #4 THEME\ViewModel\Base:__construct in /data/websites/motori/web/app/themes/motori/src/ViewModel/Single.php:18 #3 THEME\ViewModel\Single:__construct in /data/websites/motori/web/app/themes/motori/single.php:9 #2 include in /data/websites/motori/web/wp/wp-includes/template-loader.php:106 #1 require_once in /data/websites/motori/web/wp/wp-blog-header.php:19 #0 require in /data/websites/motori/web/index.php:6
Stack frames (15)
14
Whoops\Exception\ErrorException
/web/app/themes/motori/src/ViewModel/Post/Content/Image.php50
13
Whoops\Run handleError
/web/app/themes/motori/src/ViewModel/Post/Content/Image.php50
12
THEME\ViewModel\Post\Content\Image getImageData
/web/app/themes/motori/src/ViewModel/Post/Content/Image.php41
11
THEME\ViewModel\Post\Content\Image __construct
/web/app/themes/motori/src/ViewModel/Post/Components/ContentSplitters/Image.php66
10
THEME\ViewModel\Post\Components\ContentSplitters\Image getImage
/web/app/themes/motori/src/ViewModel/Post/Components/ContentSplitters/Image.php51
9
THEME\ViewModel\Post\Components\ContentSplitters\Image split
/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php110
8
THEME\ViewModel\Post\Components\SplittedContent splitSections
/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php61
7
THEME\ViewModel\Post\Components\SplittedContent init
/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php23
6
THEME\ViewModel\Post\Components\SplittedContent __construct
/web/app/themes/motori/src/ViewModel/Common/HasComponents.php15
5
THEME\ViewModel\Base loadComponents
/web/app/themes/motori/src/ViewModel/Base.php34
4
THEME\ViewModel\Base __construct
/web/app/themes/motori/src/ViewModel/Single.php18
3
THEME\ViewModel\Single __construct
/web/app/themes/motori/single.php9
2
include
/web/wp/wp-includes/template-loader.php106
1
require_once
/web/wp/wp-blog-header.php19
0
require
/web/index.php6
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Content/Image.php
   
    protected array $sizes;
    protected array $map;
 
    public function __construct(protected int $attachment_id, $sizes = ['hero'] ) {
 
        $this->sizes = $sizes;
 
        $this->map = wp_is_mobile() ? Image::MAP['mobile'] : Image::MAP['desktop'];
 
        $this->data = $this->getImageData();
    }
 
    protected function getImageData(): array {
 
        $attachment = get_post($this->attachment_id);
                
        return  [
            'id'          => $this->attachment_id,
            'title'       => $attachment->post_title,
            'sizes'       => $this->getSizes(),
            'alt'         => get_post_meta($this->attachment_id, '_wp_attachment_image_alt', true),
            'caption'     => $attachment->post_excerpt,
            'description' => $attachment->post_content,
        ];
    }
 
    
 
    protected function getUrl( $args ) : string {
 
        $url = wp_get_attachment_image_url( $this->attachment_id, 'full');
 
        return $url === false ? '' : $this->getThumborUrl( $url, $args );
    }
 
    protected function getOriginalUrl( $args ) : string {
 
        $url = wp_get_original_image_url( $this->attachment_id );
 
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Content/Image.php
   
    protected array $sizes;
    protected array $map;
 
    public function __construct(protected int $attachment_id, $sizes = ['hero'] ) {
 
        $this->sizes = $sizes;
 
        $this->map = wp_is_mobile() ? Image::MAP['mobile'] : Image::MAP['desktop'];
 
        $this->data = $this->getImageData();
    }
 
    protected function getImageData(): array {
 
        $attachment = get_post($this->attachment_id);
                
        return  [
            'id'          => $this->attachment_id,
            'title'       => $attachment->post_title,
            'sizes'       => $this->getSizes(),
            'alt'         => get_post_meta($this->attachment_id, '_wp_attachment_image_alt', true),
            'caption'     => $attachment->post_excerpt,
            'description' => $attachment->post_content,
        ];
    }
 
    
 
    protected function getUrl( $args ) : string {
 
        $url = wp_get_attachment_image_url( $this->attachment_id, 'full');
 
        return $url === false ? '' : $this->getThumborUrl( $url, $args );
    }
 
    protected function getOriginalUrl( $args ) : string {
 
        $url = wp_get_original_image_url( $this->attachment_id );
 
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Content/Image.php
            'lightbox'  => ['width' => 1920, 'height'  => 1080, 'quality' => 100 ],
            'thumbnail' => ['width' => 160,  'height'  => 120,  'quality' => 80  ],
        ]
 
    ];
 
    const DEFAULT_WIDTH   = 1200;
    const DEFAULT_HEIGHT  = 675;
    const DEFAULT_QUALITY = 80;
   
    protected array $sizes;
    protected array $map;
 
    public function __construct(protected int $attachment_id, $sizes = ['hero'] ) {
 
        $this->sizes = $sizes;
 
        $this->map = wp_is_mobile() ? Image::MAP['mobile'] : Image::MAP['desktop'];
 
        $this->data = $this->getImageData();
    }
 
    protected function getImageData(): array {
 
        $attachment = get_post($this->attachment_id);
                
        return  [
            'id'          => $this->attachment_id,
            'title'       => $attachment->post_title,
            'sizes'       => $this->getSizes(),
            'alt'         => get_post_meta($this->attachment_id, '_wp_attachment_image_alt', true),
            'caption'     => $attachment->post_excerpt,
            'description' => $attachment->post_content,
        ];
    }
 
    
 
    protected function getUrl( $args ) : string {
 
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/ContentSplitters/Image.php
        $image_id = (int) $image_split[2][0];
 
        return [
            [ 'type' => 'content', 'content' => $image_split[1][0] ],
            [ 'type' => 'image', 'content' => $this->getImage($image_id) ],
            [ 'type' => 'content', 'content' => $image_split[3][0] ]
        ];
    }
 
 
    /**
     * Return the collection of the gallery images
     * of a given gallery id
     *
     * @var int gallery_id
     * @return array
     */
    protected function getImage( $attachment_id ) : array {
 
        $image = new InTextImage( $attachment_id, ['intext', 'lightbox'] );
 
        return $image->data;
    }
}
 
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/ContentSplitters/Image.php
     * split the content section and return a new image section
     * preserve the section title, if any
     *
     * @var array $section
     * @return array that defines a section
     */
    public function split( array $section ) : array {
 
        // takes the image shortcodes and the positions they start
        $content = str_replace( ["”","″"], "\"" ,$section['content'] );
        
        preg_match_all('/(.*?){{image=(\d+)}}(.*)/s', $content, $image_split);
 
        if ( empty( $image_split[0] ) ) return [];
        
        $image_id = (int) $image_split[2][0];
 
        return [
            [ 'type' => 'content', 'content' => $image_split[1][0] ],
            [ 'type' => 'image', 'content' => $this->getImage($image_id) ],
            [ 'type' => 'content', 'content' => $image_split[3][0] ]
        ];
    }
 
 
    /**
     * Return the collection of the gallery images
     * of a given gallery id
     *
     * @var int gallery_id
     * @return array
     */
    protected function getImage( $attachment_id ) : array {
 
        $image = new InTextImage( $attachment_id, ['intext', 'lightbox'] );
 
        return $image->data;
    }
}
 
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php
     * @param Integer $post_id
     * @return mixed
     */
 
 
    /**
     * Recursive method that will progressively split the contents into
     * smaller, specialized sections according with the given splitter
     */
    protected function splitSections( Splitter $splitter) {
        
        $splits = [];
        
        // split content sections and
        // keep track of their positions in the array
        foreach ($this->sections as $idx => $section) {
            
            if ($section['type'] == 'content') {
 
                $split = $splitter->split($section);
                
                if (!empty($split)) {
                    $splits[] = (object) ['idx' => $idx , 'sections' => $split ];
                }
            }
        }
 
        // replace old sections with the splitted ones
        // starting from the bottom to the top
        foreach (array_reverse($splits) as $split) 
            array_splice($this->sections, $split->idx, 1, $split->sections);
        
            
        // no more splits exit the recursive loop
        if (empty($splits)) 
            return;
        
        // else do another ride
        else 
            $this->splitSections($splitter);
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php
 
    
    protected function init($content) {
 
        $content = strip_tags( $content, ['<h1>','<h2>','<h3>','<h4>','<h5>','<h6>','<p>','<div>','<span>','<header>','<footer>','<section>','<article>','<nav>','<aside>','<main>','<ul>','<ol>','<li>','<a>','<img>','<audio>','<video>','<source>','<table>','<thead>','<tbody>','<tfoot>','<tr>','<td>','<th>','<form>','<input>','<textarea>','<button>','<select>','<option>','<label>','<fieldset>','<legend>','<b>','<i>','<u>','<strong>','<em>','<mark>','<small>','<del>','<ins>','<script>','<noscript>','<style>','<br>','<hr>','<blockquote>','<cite>','<code>','<pre>','<abbr>','<address>','<time>']);
 
        // cambiare gli shortcode "mutati"
        foreach($this->splitters as $splitter){
 
            $content = $splitter->convertShortCode($content);
        }
 
        // initialize the content as a big section to split
        $this->sections = [
            [ 'type' => 'content', 'content' => apply_filters('the_content', $content) ]
        ];
 
        foreach ($this->splitters as $splitter) {
 
            $this->splitSections($splitter);
        }
    }
 
    
    /**
     * Return the list of the splitters
     */
    protected function getSplitters() : array {
 
        $splitters = [
            ContentSplitters\Gallery::class,
            ContentSplitters\Headings::class,
            ContentSplitters\Faq::class,
            ContentSplitters\Image::class,
            ContentSplitters\Video::class,
            ContentSplitters\Toc::class,
            ContentSplitters\InTextRelateds::class,
        ];
 
        return array_map( function( $splitter ) {  return new $splitter();  } , $splitters );
/data/websites/motori/web/app/themes/motori/src/ViewModel/Post/Components/SplittedContent.php
 
use THEME\ViewModel\Common\Component;
use THEME\ViewModel\Common\ComponentData;
use THEME\ViewModel\Post\Components\ContentSplitters;
use THEME\ViewModel\Post\Components\ContentSplitters\Splitter;
 
class SplittedContent implements Component {
 
    const NAME = 'content';
 
    protected \WP_Post $post;
 
    private $splitters;
    private $sections = [];
    
    public function __construct( $data = null, array $context = []) {
 
        $this->post = $data;
        $this->splitters = $this->getSplitters();
        $this->init( $this->post->post_content );
 
    }
 
    public function getData(): ComponentData {
 
        return new ComponentData( static::NAME, $this->getSections() );
    }
 
 
    /**
     * Expose the sections
     *
     * @return Array sections
     */
    protected function getSections() : array {
 
        return $this->sections;
    }
 
    
/data/websites/motori/web/app/themes/motori/src/ViewModel/Common/HasComponents.php
<?php
 
namespace THEME\ViewModel\Common;
 
trait HasComponents {
 
    protected array $components = [];  // list of components that yield data
    protected object|null $data = null;  // basic data of the current object ( post, term, queried_object, etc.)
    protected array $context    = [];  // data gathered while loading components from the compontent's list
    
    protected function loadComponents() {
 
        foreach($this->components as $component) {
 
            $component_instance = ( new $component( $this->data, $this->context) )->getData();
 
            $this->context[$component_instance->name] = $component_instance->data;
 
        }
    }
 
    public function getContext() {
 
        return $this->context;
    }
}
/data/websites/motori/web/app/themes/motori/src/ViewModel/Base.php
 * expose getContext() to the view
 *
 */
abstract class Base {
 
    use HasComponents;
    use CanPurgeCachedComponents;
 
 
    public function __construct() {
 
        $this->context = $this->initContext();
 
        $this->components = array_merge(
            $this->listBasicComponents(),
            $this->listComponents(),
            $this->listStructureComponents()
        );
 
        $this->loadComponents();
    }
 
 
    protected function listBasicComponents() : array {
 
        return [
            Components\Domain::class,
            Components\IsMobile::class,
            Components\AssetsDir::class,
            Components\GoogleTagManager::class,
            Components\Menus::class,
            BrandModelList::class,
        ];
    }
 
    /**
     * componenti da calcolare sempre dopo l'esecuzione degli 
     * altri hook
     *
     * @return array
/data/websites/motori/web/app/themes/motori/src/ViewModel/Single.php
<?php
 
namespace THEME\ViewModel;
 
use THEME\ViewModel\Post\Content\Templates\Template;
 
use THEME\ViewModel\Post\Components;
use THEME\ViewModel\Post\CachedComponents;
 
class Single extends Base {
 
    protected Template $postTemplate;
 
    public function __construct(\WP_Post $post, $postTemplate) {
 
        $this->data = $post;
 
        parent::__construct();
 
 
        // assign the post template (default is 'Single')
        $this->postTemplate = (new $postTemplate);
 
        // init the post template 
        // (includes content details as well)
        $this->setPost();
    }
 
 
    /**
     * Writes the basic page info
     */
    protected function initContext(): array
    {
        $context = [
            'type'        => 'single_post',
            'is_electric' =>  false,
            'infinite'    => 'false'
        ];
    
/data/websites/motori/web/app/themes/motori/single.php
<?php
 
use THEME\Utils\Timber;
use THEME\ViewModel\Single;
use THEME\ViewModel\Post\Content\Templates;
 
if (have_posts()) {
    // load data
    $data = (new Single($posts[0], Templates\Single::class))->getContext();
 
    Timber::render('single.twig', $data, 600);
}
 
/data/websites/motori/web/wp/wp-includes/template-loader.php
            }
 
            break;
        }
    }
 
    if ( ! $template ) {
        $template = get_index_template();
    }
 
    /**
     * Filters the path of the current template before including it.
     *
     * @since 3.0.0
     *
     * @param string $template The path of the template to include.
     */
    $template = apply_filters( 'template_include', $template );
    if ( $template ) {
        include $template;
    } elseif ( current_user_can( 'switch_themes' ) ) {
        $theme = wp_get_theme();
        if ( $theme->errors() ) {
            wp_die( $theme->errors() );
        }
    }
    return;
}
 
/data/websites/motori/web/wp/wp-blog-header.php
<?php
/**
 * Loads the WordPress environment and template.
 *
 * @package WordPress
 */
 
if ( ! isset( $wp_did_header ) ) {
 
    $wp_did_header = true;
 
    // Load the WordPress library.
    require_once __DIR__ . '/wp-load.php';
 
    // Set up the WordPress query.
    wp();
 
    // Load the theme template.
    require_once ABSPATH . WPINC . '/template-loader.php';
 
}
 
/data/websites/motori/web/index.php
<?php
/**
 * WordPress View Bootstrapper
 */
define('WP_USE_THEMES', true);
require __DIR__ . '/wp/wp-blog-header.php';
 
 

Environment & details:

empty
empty
empty
empty
empty
Key Value
SERVER_SOFTWARE nginx/1.26.2
REQUEST_URI /mercato-auto-italia-a-marzo-297-ecco-le-auto-piu-vendute/
USER nginx
HOME /var/lib/nginx
HTTP_REFERER https://staging.motori.it/mercato-auto-italia-a-marzo-297-ecco-le-auto-piu-vendute
HTTP_ACCEPT_ENCODING gzip, br, zstd, deflate
HTTP_USER_AGENT Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
HTTP_ACCEPT */*
HTTP_CONNECTION close
HTTP_X_FORWARDED_FOR 18.216.93.197
HTTP_HOST staging.motori.it
HTTP_X_FORWARDED_PORT 443
HTTP_X_FORWARDED_PROTO https
REDIRECT_STATUS 200
SERVER_NAME staging.motori.it
SERVER_PORT 443
SERVER_ADDR 10.50.50.197
REMOTE_PORT 39794
REMOTE_ADDR 10.50.50.12
GATEWAY_INTERFACE CGI/1.1
HTTPS on
REQUEST_SCHEME https
SERVER_PROTOCOL HTTP/1.0
DOCUMENT_ROOT /data/websites/motori/web
DOCUMENT_URI /index.php
SCRIPT_NAME /index.php
CONTENT_LENGTH
CONTENT_TYPE
REQUEST_METHOD GET
QUERY_STRING
SCRIPT_FILENAME /data/websites/motori/web/index.php
FCGI_ROLE RESPONDER
PHP_SELF /index.php
REQUEST_TIME_FLOAT 1745846031.3537
REQUEST_TIME 1745846031
WP_ENV development
WP_HOME https://staging.motori.it
WP_SITEURL https://staging.motori.it/wp
WP_DEBUG true
WP_CACHE false
WPLANG it_IT
WP_POST_REVISIONS 3
DB_NAME motori
DB_USER USRmotori
DB_PASSWORD M0t0r1.Bl4z3
DB_HOST localhost
DISABLE_WP_CRON true
ASSETS_DIR /app/themes/motori/assets
GTM_ID GTM-XXXXXX
AUTH_KEY E094PtbN,/./X6J)N>X}iRr2X@qzrp<)*n!<1kps=WXH11Ho^1)^#,,/N`RQ%4PA
SECURE_AUTH_KEY rek_-EaoK.j{F>G%}^{Kw+npcA[tI&MW0_:3|Liq*E/]U/ve{M`tRW7[,SX-?G|b
LOGGED_IN_KEY Y;LL^L-^5a5nG&?I;e[nj0<5<;&pprgWk9Eq-Ozp>H6DZwl)3LUtdxQJ-b*mXA/w
NONCE_KEY 1R!zPZ.mPoo=[i1B[dUi2a13}0:>G{BToB:OX_(S8zT+PZ7nlEn78.#t0u7x?9)&
AUTH_SALT e+WE5olbqDvW7C[%Fs}d3n#_@8^,Ha<k&}kBL|:t@*Sl2vs#qT(lw`famVOPZ:!F
SECURE_AUTH_SALT Ks7ArUFaxK%!B`mqAsS;{qC,vpR36AiIcb@N1$[2<^SVY|?11$PV&P[PPzta,N<Z
LOGGED_IN_SALT 8lh7Q,HpTM}CMRtES%GVPD--09e!MUsQbinsA0-lS=qiUFfOaC]*,R?W$Po7UMak
NONCE_SALT m%tOV5u+uWm$e4V|4!y,:K_7`-N=m$unEx>SO:VZGMS)Y!h_ln/#zTJAkM)FK7{2
Key Value
WP_ENV development
WP_HOME https://staging.motori.it
WP_SITEURL https://staging.motori.it/wp
WP_DEBUG true
WP_CACHE false
WPLANG it_IT
WP_POST_REVISIONS 3
DB_NAME motori
DB_USER USRmotori
DB_PASSWORD M0t0r1.Bl4z3
DB_HOST localhost
DISABLE_WP_CRON true
ASSETS_DIR /app/themes/motori/assets
GTM_ID GTM-XXXXXX
AUTH_KEY E094PtbN,/./X6J)N>X}iRr2X@qzrp<)*n!<1kps=WXH11Ho^1)^#,,/N`RQ%4PA
SECURE_AUTH_KEY rek_-EaoK.j{F>G%}^{Kw+npcA[tI&MW0_:3|Liq*E/]U/ve{M`tRW7[,SX-?G|b
LOGGED_IN_KEY Y;LL^L-^5a5nG&?I;e[nj0<5<;&pprgWk9Eq-Ozp>H6DZwl)3LUtdxQJ-b*mXA/w
NONCE_KEY 1R!zPZ.mPoo=[i1B[dUi2a13}0:>G{BToB:OX_(S8zT+PZ7nlEn78.#t0u7x?9)&
AUTH_SALT e+WE5olbqDvW7C[%Fs}d3n#_@8^,Ha<k&}kBL|:t@*Sl2vs#qT(lw`famVOPZ:!F
SECURE_AUTH_SALT Ks7ArUFaxK%!B`mqAsS;{qC,vpR36AiIcb@N1$[2<^SVY|?11$PV&P[PPzta,N<Z
LOGGED_IN_SALT 8lh7Q,HpTM}CMRtES%GVPD--09e!MUsQbinsA0-lS=qiUFfOaC]*,R?W$Po7UMak
NONCE_SALT m%tOV5u+uWm$e4V|4!y,:K_7`-N=m$unEx>SO:VZGMS)Y!h_ln/#zTJAkM)FK7{2
0. Whoops\Handler\PrettyPageHandler