Try with this code,
//Metabox Hook
add_action('add_meta_boxes','stars_meta_box');
//Metabox Init
function stars_meta_box(){
add_meta_box('first_name_meta_box',
'First Name','first_name_meta_box_html','spark_stars','normal','default'););
}
//Metabox Html
function first_name_meta_box_html($post){
wp_nonce_field('first_name','first_name_meta_box_nonce');
$value = get_post_meta( $post->ID, 'first_name_meta_box_key', true );
echo '<label>First Name: </label> <input type="text" name="fname" value="' . esc_attr($value) . '"/>';
}
//Save Hook
add_action( 'save_post', 'my_save_meta_box' );
//Save Metabox Value
function my_save_meta_box($post_id){
//you might want this or not
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( ! wp_verify_nonce( $_POST['first_name_meta_box_nonce'], 'first_name' ) ) {
return;
}
if(!isset($_POST["fname"])){
return;
}
update_post_meta( $post_id, "first_name_meta_box_key", sanitize_text_field($_POST["fname"]));
}
In the save function I added a couple of checks, the first one is to check if is an "AUTOSAVE", which triggers when a draft is saved, if you want to save the value of the field even on "AUTOSAVE", then delete that block.
Then, I check if the "nonce" is valid.
Then I check if the field wasn't empty.
And finally, I save the value if everything was ok, you can add something else or change the logic, but those are a few basic things to check for.
Parameters
You can see that I used parameter in the functions instead of the global "Post" object.
Data sanitization
I also added a few function to sanitize the data, with the sanitization on the save function should be enough, but, is so simple that I also escaped the value in the meta box generator function.
I think that's it, if you have any question, just ask.