Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jtn's avatar
Level 3

<? PHP tag in ajax response

I've deployed an application to Digital Ocean using Forge/Envoyer. Locally everything works fine. On the Digital Ocean server every ajax call responds with <?php prepended to the response. e.g. <?php{"data":{"id":6,"

Any clues? I've tried debugging but I'm not getting anything meaningful. The PHP side is using the following code:


    protected function respondWithItem($data, $transformer = null, $resourceKey = null)
    {
        return $this->respondWithArray($this->response->item($data, $transformer, $resourceKey));
    }

    protected function respondWithArray(array $array, array $headers = [])
    {
        $array = array_merge($array, ['meta' => $this->meta]);

        return response()->json($array, $this->statusCode, $headers);
    }

JavaScript Ajax handling is as follows:

(function() {
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')
        }
    });

    // Quickie PubSub
    var o = $({});
    $.subscribe = function() { o.on.apply(o, arguments) };
    $.unsubscribe = function() { o.off.apply(o, arguments) };
    $.publish = function() { o.trigger.apply(o, arguments) };

    // Async submit a form's input.
    var submitLaravelFormRequest = function(e) {
        var form = $(this);
        var method = form.find('input[name="_method"]').val() || 'POST';

        $.ajax({
            type: method,
            url: form.prop('action'),
            data: form.serialize(),
            success: function() {
                $.publish('ajax.formrequest.success', form);
                $.publish('ajax.request.success', form);
            }
        });

        e.preventDefault();
    };

    // Offer flash notification messages.
    // 'data-remote-success-message' => 'Yay. All Done.'
    $.subscribe('ajax.request.success', function(e, element) {
        var message = $(element).data('remote-success-message');

        if (message) {
            $('#flash').html('<div class="alert alert-success hidden-print">' + message + '</div>').fadeIn(300).delay(2500).fadeOut(300);
        }
    })

    // Handle success callbacks. To trigger Task.foo(), do:
    // 'data-model' => 'Task', 'data-remote-on-success' => 'foo'
    $.subscribe('ajax.formrequest.success', function(e, form) {
        triggerFormClickCallback.apply(form, [e, $(form).data('remote-on-success')]);
    });

    // Trigger the registered callback for a click or form submission.
    var triggerFormClickCallback = function(e, method) {
        var that = $(this);

        // What's the name of the parent model/scope/object.
        if ( ! (model = that.closest('*[data-model]').data('model'))) {
            return;
        }

        // As long as the object and method exist, trigger it and pass through the form.
        if (typeof window[model] == 'object' && typeof window[model][method] == 'function') {
            window[model][method](that);
        } else {
            console.error('Could not call method ' + method + ' on object ' + model);
        }

        e.preventDefault();
    }

    // Handle success callbacks. To trigger Task.foo(), do:
    // 'data-model' => 'Task', 'data-remote-on-success' => 'foo'
    $.subscribe('ajax.request.success', function(e, element) {
        triggerClickCallback.apply(element, [e, $(element).data('remote-on-success')]);
    });

    // Trigger the registered callback for a click or form submission.
    var triggerClickCallback = function(e, method) {
        var that = $(this);

        // As long as the object and method exist, trigger it and pass through the form.
        if (typeof window[method] == 'function') {
            window[method](that);
        } else {
            console.error('Could not call method ' + method );
        }

        e.preventDefault();
    }

    // Confirm an action before proceeding.
    var confirmAction = function(e) {
        var input = $(this);

        input.prop('disabled', 'disabled');

        bootbox.confirm(input.data('confirm'), function(result) {
            if (result == true){
                e.preventDefault();
                return true;
            }
        });

        input.removeAttr('disabled');

        return false;
    };

    // Dom bindings.
    $('form[data-remote]').on('submit', submitLaravelFormRequest);
    $('input[data-confirm], button[data-confirm]').on('click', confirmAction);
    $('*[data-click]').on('click', function(e) {
        triggerFormClickCallback.apply(this, [e, $(this).data('click')]);
    });
    //$('*[data-click-submits-form]').on('change', submitLaravelRequest);

    var displayPrintButton = function() {
        if (window.print) {
            document.write('<form><input type=button name=print value="Print" onClick="window.print()"></form>');
        }
    }

    // Async submit an ajax request.
    var submitLaravelRequest = function(e) {
        e.preventDefault();

        var element = $(this);
        var confirm = element.data('confirm');
        if (confirm !== undefined){
            bootbox.confirm(confirm, function(result) {
                if (result == true){
                    submitLaravelAjaxRequest(e, element);
                }
            });
        } else {
            submitLaravelAjaxRequest(e, element);
        }
    };

    var submitLaravelAjaxRequest = function(e, element) {
        var url = element.prop('href');
        var method = element.data('method') || 'POST';
        $.ajax({
            type: method,
            url: url,
            //data: form.serialize(),
            success: function() {
                //alert('SUCCESS');
                $.publish('ajax.request.success', element);
            },
            error: function onAjaxUpdateError (jqXHR, textStatus, errorThrown) {
                //alert(errorThrown); // TODO handle error
                $('#waitingModal').modal('hide');
                var errorModal = $('#errorModal');
                window.cloud9business.ajax.handleAjaxError(errorModal, jqXHR.responseJSON);
            }
        });
    };

    $('a[data-remote-click-submits], button[data-remote-click-submits]').on('click', submitLaravelRequest);

})();
0 likes
12 replies
jtn's avatar
Level 3

Actually, it's every response not just ajax. I thought it might have something to do with the php auto_prepend_file config setting. I set this to none as per the documentation but it hasn't fixed it.

jtn's avatar
Level 3

A simple test file containing:

Shows that the <?php tag is not prepended so this points to an application error within the Laravel app. It's just curious that this behaviour doesn't occur locally.

bashy's avatar

Check for any extra <?php in your files.

1 like
Jeffberry's avatar

I had a similar issue with my ajax requests the other day. Try running php artisan clear-compiled

jtn's avatar
Level 3

Thanks for the responses. I tried:

php artisan clear-compiled

Removing <?php tags from a few view partials.

Checking that no php files had more than one <?php tag.

Nothing helped.

Plain php files that are not processed via Laravel do not have the issue. I get the extra <?php tag for all responses generated from Laravel whether ajax or not but only in production. Locally all responses through Laravel do not have the extra <?php tag.

Running composer update both locally and in production results in a double <?php tag in the output as follows:

Writing lock file
Generating autoload files
<?php<?phpGenerating optimized class loader

Running composer diagnose does not produce the double <?php tags. They are appearing because the post-update-cmd includes two artisan commands and these are generating one <?php tag each. Running php artisan on its own does not produce the <?php tag.

Switched the APP_ENV to local and APP_DEBUG to true on the production server and the problem goes away. I get the whoops page because the local composer dependancies haven't been loaded and the whoops page does not have the problem. I do a composer update to get rid of the error and the extra <?php tag is back. So it doesn't seem to be web server settings, PHP settings or Laravel environment settings. The exception handling is unaffected but running php artisan has the problem.

jtn's avatar
Level 3

What I'm doing now is repeatedly running php artisan in the console and doing a dd('TEST') at various places. So far I've determined that the <?php tag only gets prepended after the $app->bootstrapWith method loads 'Illuminate\Foundation\Bootstrap\BootProviders'.

So it looks like either one of my or a third party service provider is not playing nicely.

jtn's avatar
jtn
OP
Best Answer
Level 3

RouteServiceProvider.php contains:

    public function map(Router $router)
    {
        $router->group(['namespace' => $this->namespace], function ($router)
        {
            foreach (File::allFiles(app_path('Http/Routes')) as $partial)
            {
                require_once($partial->getPathname());
            }
        });
    }

One of the route files loaded by the above method currently has nothing in it except for a <?php tag and NO NEW LINE. The absence of a new line was the problem. Added a comment to the file and all works perfectly. I don't know why this was more of a problem on the production server than locally. The PHP versions are both 5.6.

6 likes
jameswagoner's avatar

Literally the one post on this issue I have found.

api.php was the offending file for myself.

Thanks jtn for the lead!

iannato's avatar

Thanks I had created a route file which had nothing in it. i just added a comment and it worked in L5.3

Please or to participate in this conversation.