Updating Controller to 2.0.0

Hi everyone,

I have tried upgrading to soberwp/controller from 9.0.0-beta.4 to v2.0.0 (changing my composer.json and running “composer update”).

When I do this the controller stops working and I get the following error:

Fatal error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Class ‘App’ not found in webroot/web/app/uploads/cache/e5a9ac887cfef2523412d1d4ebb2ea91b29c2f33.php

If I comment out the App::title() call inside ‘views/partials/pager-header.blade.php’ the page loads, but my other custom controllers still do not.

I have noticed when running composer update that hassankhan/config (0.10.0) dependency is removed in the new version. When I downgrade the version back to 9.0.0-beta.4, hassankhan/config (0.10.0) is added and everything works correctly again.

I want to update so that I can use the built in ACF module in v2.0.0. Has anyone else experienced similar issues? I can’t find any info in the readme and changelog but I may have missed something.


Hi BjarneFred,

We’re also experiencing the same issue right now too. I see a thread here at https://github.com/soberwp/controller/issues/47 that seems to reference the same error. I’m able to get v2.0.0 working on my local dev server, but am experiencing the issue on our remote development server. I’ll update this thread if I can sort out a solution, but let me know if you have any insights!


There are two big changes to controller loading in 2.0.0:

  1. Namespace for Controllers has changed from to Controllers (instead of just Sage’s App).
  2. PSR-4 autoloading for controller files.

New Namespace
Nearly all stuff in Sage is already namespaced to App, and in previous releases of Controller, it operated in App as well. With 2.0.0, it’s been moved to App\Controllers.

This means that for all of your Controllers, you need to change their namespace from namespace App to namespace App\Controllers. All references to them (i.e. static methods like App::siteName() will also need to have the new namespace added (i.e. App::siteName() -> Controllers\App::siteName()).

After making these changes (or changes to file names, as below), you should probably run composer dumpautoload to force Composer to rebuild its auto-loading instructions.

PSR-4 Autoloading
This change means that you need to follow the PSR-4 rules for class, file, and directory naming. The short version is that your class names should be CamelCase and your file names should match them—so the class FrontPage should be in the files FrontPage.php.


Thanks alwaysblank. I just reviewed the namespaces and updated them where appropriate, but still seem to be generating the same error:

PHP Fatal error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Class ‘Controllers\App’ not found

I can’t say for certain because you haven’t posted any code, but the error message makes it look like you still have the wrong namespace for app/controllers/App.php. The namespace for your controllers should be App\Controllers\[whatever the controller is], but the error here says that it’s just looking for Controllers\App instead of App\Controllers\App.

If you’re struggling with namespace, you may find this post I wrote attempting to explain how they work helpful: Controller namespace issue, different behavior on localhost and development server

1 Like

This is the /app/Controllers/App.php file that we have:


  namespace App\Controllers;

  use Sober\Controller\Controller;

  class App extends Controller
  public function siteName()
    return get_bloginfo('name');

  public static function title()
    if (is_home()) {
        if ($home = get_option('page_for_posts', true)) {
            return get_the_title($home);
        return __('Latest Posts', 'sage');
    if (is_archive()) {
        return get_the_archive_title();
    if (is_search()) {
        return sprintf(__('Search Results for %s', 'sage'), get_search_query());
    if (is_404()) {
        return __('Not Found', 'sage');
    return get_the_title();

public static function siteLogo()
    $logo = get_theme_mod( 'to4_theme_logo' ); 

    if( $logo ) {
        return wp_get_attachment_image( $logo, 'thumbnail' );
    return '<span class="site-title">' . get_bloginfo( 'name' ) . '</span>';

public static function primaryNavigation()
    if (has_nav_menu('primary_navigation')) {
        return  wp_nav_menu([
                  'container' => false,
                  'menu' => __( 'Primary Navigation', 'sage' ),
                  'menu_class' => 'dropdown vertical medium-horizontal menu',
                  'theme_location' => 'primary_navigation',
                  'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',
                  // Recommend setting this to false, but if you need a fallback....
                  'fallback_cb'    => false,
                  'walker'         => new \truthout4_f6_menu_walker()
    return '<span class="">Please select a Primary Menu</span>';

public static function secondaryNavigation()
    if (has_nav_menu('secondary_navigation'))
        return wp_nav_menu([
                'container' => false,
                'menu' => __( 'Secondary Navigation', 'sage' ),
                'menu_class' => 'dropdown vertical medium-horizontal menu',
                'theme_location' => 'secondary_navigation',
                'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',
                // Recommend setting this to false, but if you need a fallback....
                'fallback_cb'    => false,
                'walker'         => new \truthout4_f6_menu_walker()
    return '<span class="">Please select a Secondary Menu</span>';  

 * Setup sitewide variables for featured articles.
 * @return $data key => value array for use in blade templates.
public function featuredArticles() 
    $data = [];

    $featured_articles = get_option( 'options_to4_featured_posts' );

    foreach ($featured_articles as $article) {

        $terms = get_the_terms( $article, 'section' );

        if ( $terms && !is_wp_error( $terms ) ) {
            $term_array = array_pop($terms);
            $term = $term_array->name;
        } else {
            $term = '';

        $author_names = [];

        // Retrieve article authors post meta from each $article post
        $authors = get_post_meta( $article, 'to4_related_authors', true );

        // If authors exist and is not wp_error...
        if ( $authors && !is_wp_error( $authors ) ) {
            // Iterate through array and retrieve author name, populate array with names.
            foreach( $authors as $author ) {
                $author_name = get_the_title($author);
                array_push($author_names, $author_name);
            // Convert array values into string for use in our array object below.
            $author_names = implode(', ', $author_names);

        $this_post = (object) [
            'medium_thumb' => get_the_post_thumbnail($article, 'sd_thumbnail', ['class' => '']),
            'large_thumb' => get_the_post_thumbnail($article, 'sd_medium', ['class' => '']),
            'permalink' => get_the_permalink($article),
            'title' => get_the_title($article),
            'short_title' => mb_strimwidth( get_the_title($article), 0, 40, '&hellip;' ),
            'excerpt' => get_the_excerpt($article),
            'term' => $term,
            'date' => get_the_date('F j, Y', $article),
            'authors' => $author_names
        array_push($data, $this_post);

    // Only retrieve the first 4 posts for the homepage
    if( is_front_page() ) {
        $data = array_slice($data, 0, 4);

      return $data;

Have I formatted it incorrectly somewhere?

And in header.blade.php

I’m referencing the static methods like this:

1 Like

Do your static methods work if you write them like so:


Have you run composer dumpautoload from your theme root?

1 Like

For some reason the reply truncated the code, but yes, I just tested that right before you responded and it seems to work now.

1 Like

Thank you @alwaysblank for the thorough response. After making the changes you suggested we are back up and running. Cheers.

1 Like

I’m working with tanog.

We found a work-around: the files app.php and FrontPage.php must be located in both:


If any file is missing from either location the site goes down with one of two PHP errors.

We’ve run composer dumpautoload to no effect and a review of the autoload file showed nothing but sniff stuff.

The only two functions we use in templates are App\Controllers\App::title() and App\Controllers\App::siteLogo();, plus ACF fields with values assigned in the controller.

We started this project on Sage 9 b4. Is there some other dependency that needs to be updated when upgrading to Sober Controller to 2.0.0?

Hey guys,

I got ahead of myself. I spun up a test site and only checked a static method. Static methods worked as expected so I thought the issue was resolved. No matter what I tried I couldn’t get the public methods to work properly (variables in the blade views).

I tried changing the autoloader psr-4 settings in composer.json, dumping autoloader, lots of variations in capatalisation for folder names and class names etc.

I didn’t try the hacky fix of having duplicate files in multiple controller directories though.

For now I have reverted back to “~9.0.0-beta.4”


We solved the problem of multiple controller files by using a little git dances of moving, deleting, and checking out files. Now we have only Controllers/

But like you now only static functions work. No public functions or ACF variables are passed. Like you everything works on localhost but fails on the staging site.

It’s like the variables are being sent to some other namespace but I don’t know how to check a variable value when I don’t know it’s name.

To be clear, here’s app/Controllers/app.php

namespace App\Controllers;

use Sober\Controller\Controller;

class App extends Controller
  protected $acf = true;

  public function siteName()
    return get_bloginfo('name');
[more stuff]

Here’s partials/content-single.blade.php which is called from single.blade.php

@dump( $to4_related_posts )<br>
@dump( get_field( to4_related_posts ) )<br>
{!! App\Controllers\App::siteName() !!}

Here’s the output:

array(2) { [0]=> int(216777) [1]=> int(217085) }
My Site Name



In composer.json replace line 31 with:

  "App\\": "app/",
  "App\\Controllers\\": "app/controllers/"

As per this link at soberwp.

composer dump-autoload

Run (optional?)
yarn build

These steps seemed to have solved my empty ACF and namespaced variables issue. I have no idea why; namespacing is a black art to me.

Note 1: if you are working on a caps-insensitive system like OSX and pushing to a caps-senstive server like linux you might have additional issues with duplicate filenames. Use the file explorer in Github or wherever you keep your repo to check for duplicate but case-changed versions of folders or files in app/ (i.e. controllers and Controllers, single.php and Single.php

Note 2: all controller names must be CamelCaps versions of the template names with dashes removed —front-page.blade.php becomes FrontPage.php

Note 3: Adding this to your ~/.gitconfig might stop case insensitivity issues on OSX:

	ignorecase = false

@slam this is really useful! I have managed to solve my very similar issue now by following some of the things you mentioned! Basically for me the light-bulb moment was the case insensitivity of Mac, so checking my staging site I realised that App.php had been renamed app.php on deployment!


Yeah, this case-insensitivity is news to me too. But you can see it in action by capitalizing any file in your repo and seeing that git status reports there’s nothing to commit.

Ditto on pulls; if your repo has foo and your Mac has Foo then a git checkout will accept Foo as the correct folder. That can work in your favor; so long as you get the repo in order it might not matter what case your Mac uses. Mac case changes are hard to stage to git.


Sober Controller 2.0.1 might solve this issue and allow the controller folder to be case-insensitive. Update with:

composer update soberwp/controller

Here’s the thread.

1 Like

Sorry for going quiet on you @slam

2.0.1 has fixed this for me. Thanks for everything!

I wasn’t expecting an answer after posting the solution. Glad it worked out!

And maybe this is a reminder to us both to not adopt dot-oh dependencies.

Just in case it helps anyone, I had to do the following, when upgrading to 2.0.0+ to satisfy the new PSR-4 rules:

  • Rename the app/Controllers directory to app/Controllers
  • Now rename the app/Controllers/partials directory to app/Controllers/Partials

To rename, I needed to do git mv and sometimes I needed to do it twice, use a temporary name:

git mv file fileA
git mv fileA File

If you change these names then you don’t have to add anything to composer.json - no need to add a second line for app/controllers/ because it can now be found using the expected capitalisation.

Source: https://stackoverflow.com/a/19956280/445547

  • Then the all-important composer dump-autoload.

  • I also needed to do a git reset --hard <branch> on my staging server before pulling in the changes

  • If you run into problems as I did, make sure you’re correctly upgrading to version 2.0.x by doing composer install or composer update on the sage directory.

Careful with this; it can create problems and doesn’t’ solve the issue here.

On MacOS and Windows, make sure your git settings have core.ignorecase=true as the opposite can create git havoc.

Source: this comment https://stackoverflow.com/questions/17683458/how-do-i-commit-case-sensitive-only-filename-changes-in-git#comment45149115_17688308