2

and thank you for any help you can provide. I've been trying to address this issue for a couple of days now, but I've found myself out of my depth... any help would be most appreciated--this is my first time trying to create a plugin, and I'm very new to PHP.

I have a custom plugin, cobbled together from Joshua David Nelson's Weather in Wordpress with DarkSky article and this video series from CodeTime. It works really well, except when I try to use the shortcode on a page twice (it is embedded in the menu, and trying to use the shortcode again on a page results in an error). I'm trying to display weather information in a different way on the homepage, and thought I could use the same sort of function/shortcode for it.

Here are the plugin contents, minus the coordinates and API key:

function weather_station(){

    $coordinates = 'XXXX'; //coordinates are here
    $api_key = 'XXXX'; //api key is here

    $api_url = 'https://api.darksky.net/forecast/'.$api_key.'/'.$coordinates;

    $cache_key = md5( 'remote_request|' . $api_url );
    $forecast_request = get_transient( $cache_key );

    if ( false === $forecast_request ) {
        $forecast_request = wp_remote_get( $api_url );

        if ( is_wp_error( $forecast_request ) ) {
            // Cache failures for a short time, will speed up page rendering in the event of remote failure.
            set_transient( $cache_key, $forecast_request, 60 );
            return false;
        }
        // Success, cache for a longer time.
        set_transient( $cache_key, $forecast_request, 300 );
    }

    if ( is_wp_error( $forecast_request ) ) {
        return false;
    }

    $body = wp_remote_retrieve_body( $forecast_request );

    $forecast = json_decode( $body );
    //$forecast = json_decode(file_get_contents($api_url));


    $icon_currently = $forecast->currently->icon;
    $temperature_currently = round( $forecast->currently->temperature );
    $summary_hourly = $forecast->hourly->summary;

    // Set the default timezone
    date_default_timezone_set($forecast->timezone);

    // Get the appropriate icon
    function get_icon($icon) {
        if($icon==='clear-day') {
            $the_icon = '<i class="fas fa-sun"></i>';
            return $the_icon;
        }
        elseif($icon==='clear-night') {
            $the_icon = '<i class="fas fa-moon-stars"></i>';
            return $the_icon;
        }
        elseif($icon==='rain') {
            $the_icon = '<i class="fas fa-cloud-showers-heavy"></i>';
            return $the_icon;
        }
        elseif($icon==='snow') {
            $the_icon = '<i class="fas fa-cloud-snow"></i>';
            return $the_icon;
        }
        elseif($icon==='sleet') {
            $the_icon = '<i class="fas fa-cloud-sleet"></i>';
            return $the_icon;
        }
        elseif($icon==='wind') {
            $the_icon = '<i class="fas fa-wind"></i>';
            return $the_icon;
        }
        elseif($icon==='fog') {
            $the_icon = '<i class="fas fa-fog"></i>';
            return $the_icon;
        }
        elseif($icon==='cloudy') {
            $the_icon = '<i class="fas fa-clouds"></i>';
            return $the_icon;
        }
        elseif($icon==='partly-cloudy-day') {
            $the_icon = '<i class="fas fa-clouds-sun"></i>';
            return $the_icon;
        }
        elseif($icon==='partly-cloudy-night') {
            $the_icon = '<i class="fas fa-clouds-moon"></i>';
            return $the_icon;
        }
        elseif($icon==='hail') {
            $the_icon = '<i class="fas fa-cloud-hail"></i>';
            return $the_icon;
        }
        elseif($icon==='thunderstorm') {
            $the_icon = '<i class="fas fa-thunderstorm"></i>';
            return $the_icon;
        }
        elseif($icon==='tornado') {
            $the_icon = '<i class="fas fa-tornado"></i>';
            return $the_icon;
        }
        else {
            $the_icon = '<i class="fas fa-thermometer-half"></i>';
            return $the_icon;
        }
    }

?>

<div class="weather-station">
    <div class="weather-station-button">
        <span class="weather-station-icon"><?php echo get_icon($forecast->currently->icon) ?></span>
        <span class="weather-station-temperature-currently"><?php echo $temperature_currently ?>&deg;</span>
    </div>
    <div class="weather-station-details">
        <p class="weather-station-details-title">Forecast</p>
        <?php

            // Start the foreach loop to get hourly forecast
            foreach($forecast->daily->data as $day):

        ?>

        <p class="weather-station-details-temperature-range"><?php echo round($day->temperatureLow).'&deg;/'.round($day->temperatureHigh).'&deg;'; ?></p>

        <?php
            // Break because we got today's low/high
            break;

            // End the foreach loop
            endforeach;

        ?>

        <p class="weather-station-details-summary"><?php echo $summary_hourly ?></p>

        <?php if (!empty($forecast->alerts)) { ?>

            <ul class="weather-station-details-alerts">

                <?php

                    // Start the foreach loop to get hourly forecast
                    foreach($forecast->alerts as $alert):

                ?>

                <li><a href="<?php echo $alert->uri ?>"><?php echo $alert->title ?></a><br><span>expires <?php echo date("g:i a", $alert->expires) ?></span></li>

                <?php

                    // End the foreach loop
                    endforeach;

                ?>

            </ul>

        <?php } ?>

        <p class="weather-station-details-darksky"><a href="https://darksky.net/poweredby/" target="_blank">Powered by Dark Sky</a></p>
    </div>


</div>

<?php }

add_shortcode('go_weather_station', 'weather_station');

I turned on wp_debug, and it shows me where the issue is--I'm just not sure of the best way to fix it. The error message is:

Fatal error: Cannot redeclare get_icon() (previously declared in /www/wp-content/plugins/go-weather/go-weather.php:52) in /www/wp-content/plugins/go-weather/go-weather.php on line 52

I've since read that I should not declare a function within a function, which I'm doing at line 52, but I have tried moving it to no avail (the plugin doesn't seem to work at all). I'd be grateful for any tips; I'll be working to solve the issue myself as well, but if anyone is willing to give me a boost, that would be amazing. Thanks very much.

  • 1
    You're not meant to echo in a shortcode, shortcodes return their content as a string, that shortcode is implemented incorrectly. And you are correct, you must never nest functions like that – Tom J Nowell Dec 18 '19 at 1:25
2

As @TomJNowell said, You must never nest functions like that. so First,you have to move that functions out of that shortcode function and paste separately in your plugin's file or active themes functions.php file. Second, follow what the codex says Shortcodes, If the shortcode produces a lot of HTML then ob_start can be used to capture output and convert it to a string. I have tested using two shortcode in page and it is working fine https://prnt.sc/qceew0

function weather_station(){
    ob_start();
    $coordinates = 'XXXX'; //coordinates are here
    $api_key = 'XXXXXX'; //api key is here
    $api_url = 'https://api.darksky.net/forecast/'.$api_key.'/'.$coordinates;
    $cache_key = md5( 'remote_request|' . $api_url );
    $forecast_request = get_transient( $cache_key );

    if ( false === $forecast_request ) {
        $forecast_request = wp_remote_get( $api_url );
        if ( is_wp_error( $forecast_request ) ) {
            // Cache failures for a short time, will speed up page rendering in the event of remote failure.
            set_transient( $cache_key, $forecast_request, 60 );
            return false;
        }
        // Success, cache for a longer time.
        set_transient( $cache_key, $forecast_request, 300 );
    }

    if ( is_wp_error( $forecast_request ) ) {
        return false;
    }
    $body = wp_remote_retrieve_body( $forecast_request );
    $forecast = json_decode( $body );
    //$forecast = json_decode(file_get_contents($api_url));

    $icon_currently = $forecast->currently->icon;
    $temperature_currently = round( $forecast->currently->temperature );
    $summary_hourly = $forecast->hourly->summary;

    // Set the default timezone
    date_default_timezone_set($forecast->timezone);

    ?>
    <div class="weather-station">
        <div class="weather-station-button">
            <span class="weather-station-icon"><?php echo get_icon($forecast->currently->icon) ?></span>
            <span class="weather-station-temperature-currently"><?php echo $temperature_currently ?>&deg;</span>
        </div>
        <div class="weather-station-details">
            <p class="weather-station-details-title">Forecast</p>
            <?php
                // Start the foreach loop to get hourly forecast
                foreach($forecast->daily->data as $day):
            ?>

            <p class="weather-station-details-temperature-range"><?php echo round($day->temperatureLow).'&deg;/'.round($day->temperatureHigh).'&deg;'; ?></p>
            <?php
                // Break because we got today's low/high
                break;
                // End the foreach loop
                endforeach;
            ?>
            <p class="weather-station-details-summary"><?php echo $summary_hourly ?></p>
            <?php if (!empty($forecast->alerts)) { ?>
                <ul class="weather-station-details-alerts">
                    <?php
                        // Start the foreach loop to get hourly forecast
                        foreach($forecast->alerts as $alert):
                    ?>
                    <li><a href="<?php echo $alert->uri ?>"><?php echo $alert->title ?></a><br><span>expires <?php echo date("g:i a", $alert->expires) ?></span></li>
                    <?php
                        // End the foreach loop
                        endforeach;
                    ?>
                </ul>
            <?php } ?>
            <p class="weather-station-details-darksky"><a href="https://darksky.net/poweredby/" target="_blank">Powered by Dark Sky</a></p>
        </div>
    </div>
    <?php 
   return ob_get_clean();
}
add_shortcode('go_weather_station', 'weather_station');

// Get the appropriate icon
function get_icon($icon) {
    if($icon==='clear-day') {
        $the_icon = '<i class="fas fa-sun"></i>';
        return $the_icon;
    }
    elseif($icon==='clear-night') {
        $the_icon = '<i class="fas fa-moon-stars"></i>';
        return $the_icon;
    }
    elseif($icon==='rain') {
        $the_icon = '<i class="fas fa-cloud-showers-heavy"></i>';
        return $the_icon;
    }
    elseif($icon==='snow') {
        $the_icon = '<i class="fas fa-cloud-snow"></i>';
        return $the_icon;
    }
    elseif($icon==='sleet') {
        $the_icon = '<i class="fas fa-cloud-sleet"></i>';
        return $the_icon;
    }
    elseif($icon==='wind') {
        $the_icon = '<i class="fas fa-wind"></i>';
        return $the_icon;
    }
    elseif($icon==='fog') {
        $the_icon = '<i class="fas fa-fog"></i>';
        return $the_icon;
    }
    elseif($icon==='cloudy') {
        $the_icon = '<i class="fas fa-clouds"></i>';
        return $the_icon;
    }
    elseif($icon==='partly-cloudy-day') {
        $the_icon = '<i class="fas fa-clouds-sun"></i>';
        return $the_icon;
    }
    elseif($icon==='partly-cloudy-night') {
        $the_icon = '<i class="fas fa-clouds-moon"></i>';
        return $the_icon;
    }
    elseif($icon==='hail') {
        $the_icon = '<i class="fas fa-cloud-hail"></i>';
        return $the_icon;
    }
    elseif($icon==='thunderstorm') {
        $the_icon = '<i class="fas fa-thunderstorm"></i>';
        return $the_icon;
    }
    elseif($icon==='tornado') {
        $the_icon = '<i class="fas fa-tornado"></i>';
        return $the_icon;
    }
    else {
        $the_icon = '<i class="fas fa-thermometer-half"></i>';
        return $the_icon;
    }
}
  • 1
    Wow, thank you so much, Chetan. This was extremely helpful, and it taught me a lot. Much appreciated--this solution is perfect. – Grant Olson Dec 18 '19 at 16:25
0

you can factorise the "get_icon" function like that

function get_icon($icon) {

    $names = [
        "clear-day" => "sun",
        "clear-night" => "moon-stars",
        "rain" => "cloud-showers-heavy",
        "snow" => "cloud-snow",
        "sleet" => "cloud-sleet",
        "wind" => "wind",
        "fog" => "fog",
        "cloudy" => "clouds",
        "partly-cloudy-day" => "clouds-sun",
        "partly-cloudy-night" => "clouds-moon",
        "hail" => "cloud-hail",
        "thunderstorm" => "thunderstorm",
        "tornado" => "tornado",
    ];

    $value = $names[$icon] ?? "thermometer-half";


    return "<i class=\"fas fa-{$value}\"></i>";

}
  • Thanks for this tip too, Kaperto. This definitely cleaned things up--much appreciated. – Grant Olson Dec 18 '19 at 19:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.