Can one create multiple Custom Post Types with a for loop?

You have a problem with scopes in your code and you misuse actions, I guess.

Let me explain… Let’s take a look at this part of code:

for($i =1; $i <= $options_foo['num_post_types']; $i++) {
    global $options_foo;
    $singular = $options_foo['post_type_names'][$i]['singular'];
    $plural = $options_foo['post_type_names'][$i]['plural'];
    add_action('init', function(){
        global $singular;

What it does, is:

  1. You loop through all options and for every one of them:
    a) You set some global variables
    b) You register new action to init hook.

So some time later, the hook init causes your actions to be called one by one. But all of these actions will use the same value of the global variables (because they were set long before…)

Here’s how it should be done:

add_action( 'init', function () {
    global $options_foo;

    if ( $options_foo['num_post_types'] > 0 ) {
        for ( $i=1; $i <= $options_foo['num_post_types']; $i++) {
            $singular = $options_foo['post_type_names'][$i]['singular'];
            $plural = $options_foo['post_type_names'][$i]['plural'];

            $labels = array(
                'name'                  => $plural,
                'singular_name'         => $singular,
                'add_new'               => "New $singular",
                'add_new_item'          => "New $singular",
                'edit_item'             => "Edit $singular",
                'new_item'              => "New $singular",
                'view_item'             => "View $singular",
                'view_items'            => "View $plural",
                'search_items'          => "Search $plural",
                "not_found"             => "No $plural Found",
                "not_found_in_trash"    => "No $plural Found in Trash",
                'all_items'             => "All $plural",
                'attributes'            => "$singular Attributes",
                'insert_into_item'      => "Insert to $singular",
                'uploaded_to_this_item' => "Uploaded to this $singular"
            );
            $supports = array('title', 'thumbnail');
            $args = array(
                'labels'                => $labels,
                'public'                => true,
                'exclude_from_search'   => true,
                'publicly_queryable'    => true,
                'show_in_nav_menus'     => false,
                'show_in_admin_bar'     => false,
                'menu_position'         => 5,
                'menu_icon'             => 'dashicons-admin-home',
                'supports'              => $supports,
                'can_export'            => 'true'
            );
            register_post_type($singular, $args);
        }
    }
});