Admin: global $post empty

The basic mistake is, that every data is available at the point where you write your code. That isn’t the case and hooks are there to provide access to the core code during different states of the core loading procedure graph.

In short and very simplified things run like this:

# core loads
// ...more stuff...
# point at where all must-use-plugins are loaded - first entry point
do_action( 'muplugins_loaded' );
// ...more stuff...
# point at where all plugins are loaded - second entry point
do_action( 'plugins_loaded' );
// ...more stuff...
# main core stuff available - use this hook instead of `init` for multisite plugins
do_action( 'wp_loaded' );
// ...more stuff...
# themes functions.php files loaded - use this hook for theme stuff
do_action( 'after_setup_theme' );
// ...rendering happens...

Somewhere in between, WP core sets up (fills with data) different globally available datasets in global variables. Some are defined during the rendering flow, others before it, etc. In other words, when you write a plugin and just drop code in the wild (not in a callback attached to a hook or filter), then it’s very uncertain where it will appear. In your example possibly before plugins_loaded is called. The result is either a PHP error (undefined variable) or just something empty, because the variable wasn’t filled.

Conclusion: Always! put your code in callbacks that are attached to (the appropriate) hooks and filters. In most cases this means a bit of shotgun debugging and attaching your callbacks to different hooks/filters that you find when following the core loading procedure with reading core files.

Short cut to data available in the admin and on which hook it is available via the wecodemore/”Current Admin Info”-Plugin.