wp_insert_post( array $postarr, bool $wp_error = false )

Insert or update a post.


Description Description

If the $postarr parameter has ‘ID’ set to a value, then post will be updated.

You can set the post date manually, by setting the values for ‘post_date’ and ‘post_date_gmt’ keys. You can close the comments or open the comments by setting the value for ‘comment_status’ key.

See also See also


Top ↑

Parameters Parameters

$postarr

(array) (Required) An array of elements that make up a post to update or insert.

  • 'ID'
    (int) The post ID. If equal to something other than 0, the post with that ID will be updated. Default 0.
  • 'post_author'
    (int) The ID of the user who added the post. Default is the current user ID.
  • 'post_date'
    (string) The date of the post. Default is the current time.
  • 'post_date_gmt'
    (string) The date of the post in the GMT timezone. Default is the value of $post_date.
  • 'post_content'
    (mixed) The post content. Default empty.
  • 'post_content_filtered'
    (string) The filtered post content. Default empty.
  • 'post_title'
    (string) The post title. Default empty.
  • 'post_excerpt'
    (string) The post excerpt. Default empty.
  • 'post_status'
    (string) The post status. Default 'draft'.
  • 'post_type'
    (string) The post type. Default 'post'.
  • 'comment_status'
    (string) Whether the post can accept comments. Accepts 'open' or 'closed'. Default is the value of 'default_comment_status' option.
  • 'ping_status'
    (string) Whether the post can accept pings. Accepts 'open' or 'closed'. Default is the value of 'default_ping_status' option.
  • 'post_password'
    (string) The password to access the post. Default empty.
  • 'post_name'
    (string) The post name. Default is the sanitized post title when creating a new post.
  • 'to_ping'
    (string) Space or carriage return-separated list of URLs to ping. Default empty.
  • 'pinged'
    (string) Space or carriage return-separated list of URLs that have been pinged. Default empty.
  • 'post_modified'
    (string) The date when the post was last modified. Default is the current time.
  • 'post_modified_gmt'
    (string) The date when the post was last modified in the GMT timezone. Default is the current time.
  • 'post_parent'
    (int) Set this for the post it belongs to, if any. Default 0.
  • 'menu_order'
    (int) The order the post should be displayed in. Default 0.
  • 'post_mime_type'
    (string) The mime type of the post. Default empty.
  • 'guid'
    (string) Global Unique ID for referencing the post. Default empty.
  • 'post_category'
    (array) Array of category IDs. Defaults to value of the 'default_category' option.
  • 'tags_input'
    (array) Array of tag names, slugs, or IDs. Default empty.
  • 'tax_input'
    (array) Array of taxonomy terms keyed by their taxonomy name. Default empty.
  • 'meta_input'
    (array) Array of post meta values keyed by their post meta key. Default empty.

$wp_error

(bool) (Optional) Whether to return a WP_Error on failure.

Default value: false


Top ↑

Return Return

(int|WP_Error) The post ID on success. The value 0 or WP_Error on failure.


Top ↑

Source Source

File: wp-includes/post.php

3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
function wp_insert_post( $postarr, $wp_error = false ) {
    global $wpdb;
 
    $user_id = get_current_user_id();
 
    $defaults = array(
        'post_author'           => $user_id,
        'post_content'          => '',
        'post_content_filtered' => '',
        'post_title'            => '',
        'post_excerpt'          => '',
        'post_status'           => 'draft',
        'post_type'             => 'post',
        'comment_status'        => '',
        'ping_status'           => '',
        'post_password'         => '',
        'to_ping'               => '',
        'pinged'                => '',
        'post_parent'           => 0,
        'menu_order'            => 0,
        'guid'                  => '',
        'import_id'             => 0,
        'context'               => '',
    );
 
    $postarr = wp_parse_args( $postarr, $defaults );
 
    unset( $postarr['filter'] );
 
    $postarr = sanitize_post( $postarr, 'db' );
 
    // Are we updating or creating?
    $post_ID = 0;
    $update  = false;
    $guid    = $postarr['guid'];
 
    if ( ! empty( $postarr['ID'] ) ) {
        $update = true;
 
        // Get the post ID and GUID.
        $post_ID     = $postarr['ID'];
        $post_before = get_post( $post_ID );
        if ( is_null( $post_before ) ) {
            if ( $wp_error ) {
                return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) );
            }
            return 0;
        }
 
        $guid            = get_post_field( 'guid', $post_ID );
        $previous_status = get_post_field( 'post_status', $post_ID );
    } else {
        $previous_status = 'new';
    }
 
    $post_type = empty( $postarr['post_type'] ) ? 'post' : $postarr['post_type'];
 
    $post_title   = $postarr['post_title'];
    $post_content = $postarr['post_content'];
    $post_excerpt = $postarr['post_excerpt'];
    if ( isset( $postarr['post_name'] ) ) {
        $post_name = $postarr['post_name'];
    } elseif ( $update ) {
        // For an update, don't modify the post_name if it wasn't supplied as an argument.
        $post_name = $post_before->post_name;
    }
 
    $maybe_empty = 'attachment' !== $post_type
        && ! $post_content && ! $post_title && ! $post_excerpt
        && post_type_supports( $post_type, 'editor' )
        && post_type_supports( $post_type, 'title' )
        && post_type_supports( $post_type, 'excerpt' );
 
    /**
     * Filters whether the post should be considered "empty".
     *
     * The post is considered "empty" if both:
     * 1. The post type supports the title, editor, and excerpt fields
     * 2. The title, editor, and excerpt fields are all empty
     *
     * Returning a truthy value to the filter will effectively short-circuit
     * the new post being inserted, returning 0. If $wp_error is true, a WP_Error
     * will be returned instead.
     *
     * @since 3.3.0
     *
     * @param bool  $maybe_empty Whether the post should be considered "empty".
     * @param array $postarr     Array of post data.
     */
    if ( apply_filters( 'wp_insert_post_empty_content', $maybe_empty, $postarr ) ) {
        if ( $wp_error ) {
            return new WP_Error( 'empty_content', __( 'Content, title, and excerpt are empty.' ) );
        } else {
            return 0;
        }
    }
 
    $post_status = empty( $postarr['post_status'] ) ? 'draft' : $postarr['post_status'];
    if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash', 'auto-draft' ), true ) ) {
        $post_status = 'inherit';
    }
 
    if ( ! empty( $postarr['post_category'] ) ) {
        // Filter out empty terms.
        $post_category = array_filter( $postarr['post_category'] );
    }
 
    // Make sure we set a valid category.
    if ( empty( $post_category ) || 0 == count( $post_category ) || ! is_array( $post_category ) ) {
        // 'post' requires at least one category.
        if ( 'post' == $post_type && 'auto-draft' != $post_status ) {
            $post_category = array( get_option( 'default_category' ) );
        } else {
            $post_category = array();
        }
    }
 
    /*
     * Don't allow contributors to set the post slug for pending review posts.
     *
     * For new posts check the primitive capability, for updates check the meta capability.
     */
    $post_type_object = get_post_type_object( $post_type );
 
    if ( ! $update && 'pending' === $post_status && ! current_user_can( $post_type_object->cap->publish_posts ) ) {
        $post_name = '';
    } elseif ( $update && 'pending' === $post_status && ! current_user_can( 'publish_post', $post_ID ) ) {
        $post_name = '';
    }
 
    /*
     * Create a valid post name. Drafts and pending posts are allowed to have
     * an empty post name.
     */
    if ( empty( $post_name ) ) {
        if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
            $post_name = sanitize_title( $post_title );
        } else {
            $post_name = '';
        }
    } else {
        // On updates, we need to check to see if it's using the old, fixed sanitization context.
        $check_name = sanitize_title( $post_name, '', 'old-save' );
        if ( $update && strtolower( urlencode( $post_name ) ) == $check_name && get_post_field( 'post_name', $post_ID ) == $check_name ) {
            $post_name = $check_name;
        } else { // new post, or slug has changed.
            $post_name = sanitize_title( $post_name );
        }
    }
 
    /*
     * If the post date is empty (due to having been new or a draft) and status
     * is not 'draft' or 'pending', set date to now.
     */
    if ( empty( $postarr['post_date'] ) || '0000-00-00 00:00:00' == $postarr['post_date'] ) {
        if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
            $post_date = current_time( 'mysql' );
        } else {
            $post_date = get_date_from_gmt( $postarr['post_date_gmt'] );
        }
    } else {
        $post_date = $postarr['post_date'];
    }
 
    // Validate the date.
    $mm         = substr( $post_date, 5, 2 );
    $jj         = substr( $post_date, 8, 2 );
    $aa         = substr( $post_date, 0, 4 );
    $valid_date = wp_checkdate( $mm, $jj, $aa, $post_date );
    if ( ! $valid_date ) {
        if ( $wp_error ) {
            return new WP_Error( 'invalid_date', __( 'Invalid date.' ) );
        } else {
            return 0;
        }
    }
 
    if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) {
        if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) {
            $post_date_gmt = get_gmt_from_date( $post_date );
        } else {
            $post_date_gmt = '0000-00-00 00:00:00';
        }
    } else {
        $post_date_gmt = $postarr['post_date_gmt'];
    }
 
    if ( $update || '0000-00-00 00:00:00' == $post_date ) {
        $post_modified     = current_time( 'mysql' );
        $post_modified_gmt = current_time( 'mysql', 1 );
    } else {
        $post_modified     = $post_date;
        $post_modified_gmt = $post_date_gmt;
    }
 
    if ( 'attachment' !== $post_type ) {
        if ( 'publish' == $post_status ) {
            $now = gmdate( 'Y-m-d H:i:59' );
            if ( mysql2date( 'U', $post_date_gmt, false ) > mysql2date( 'U', $now, false ) ) {
                $post_status = 'future';
            }
        } elseif ( 'future' == $post_status ) {
            $now = gmdate( 'Y-m-d H:i:59' );
            if ( mysql2date( 'U', $post_date_gmt, false ) <= mysql2date( 'U', $now, false ) ) {
                $post_status = 'publish';
            }
        }
    }
 
    // Comment status.
    if ( empty( $postarr['comment_status'] ) ) {
        if ( $update ) {
            $comment_status = 'closed';
        } else {
            $comment_status = get_default_comment_status( $post_type );
        }
    } else {
        $comment_status = $postarr['comment_status'];
    }
 
    // These variables are needed by compact() later.
    $post_content_filtered = $postarr['post_content_filtered'];
    $post_author           = isset( $postarr['post_author'] ) ? $postarr['post_author'] : $user_id;
    $ping_status           = empty( $postarr['ping_status'] ) ? get_default_comment_status( $post_type, 'pingback' ) : $postarr['ping_status'];
    $to_ping               = isset( $postarr['to_ping'] ) ? sanitize_trackback_urls( $postarr['to_ping'] ) : '';
    $pinged                = isset( $postarr['pinged'] ) ? $postarr['pinged'] : '';
    $import_id             = isset( $postarr['import_id'] ) ? $postarr['import_id'] : 0;
 
    /*
     * The 'wp_insert_post_parent' filter expects all variables to be present.
     * Previously, these variables would have already been extracted
     */
    if ( isset( $postarr['menu_order'] ) ) {
        $menu_order = (int) $postarr['menu_order'];
    } else {
        $menu_order = 0;
    }
 
    $post_password = isset( $postarr['post_password'] ) ? $postarr['post_password'] : '';
    if ( 'private' == $post_status ) {
        $post_password = '';
    }
 
    if ( isset( $postarr['post_parent'] ) ) {
        $post_parent = (int) $postarr['post_parent'];
    } else {
        $post_parent = 0;
    }
 
    $new_postarr = array_merge(
        array(
            'ID' => $post_ID,
        ),
        compact( array_diff( array_keys( $defaults ), array( 'context', 'filter' ) ) )
    );
 
    /**
     * Filters the post parent -- used to check for and prevent hierarchy loops.
     *
     * @since 3.1.0
     *
     * @param int   $post_parent Post parent ID.
     * @param int   $post_ID     Post ID.
     * @param array $new_postarr Array of parsed post data.
     * @param array $postarr     Array of sanitized, but otherwise unmodified post data.
     */
    $post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, $new_postarr, $postarr );
 
    /*
     * If the post is being untrashed and it has a desired slug stored in post meta,
     * reassign it.
     */
    if ( 'trash' === $previous_status && 'trash' !== $post_status ) {
        $desired_post_slug = get_post_meta( $post_ID, '_wp_desired_post_slug', true );
        if ( $desired_post_slug ) {
            delete_post_meta( $post_ID, '_wp_desired_post_slug' );
            $post_name = $desired_post_slug;
        }
    }
 
    // If a trashed post has the desired slug, change it and let this post have it.
    if ( 'trash' !== $post_status && $post_name ) {
        wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_ID );
    }
 
    // When trashing an existing post, change its slug to allow non-trashed posts to use it.
    if ( 'trash' === $post_status && 'trash' !== $previous_status && 'new' !== $previous_status ) {
        $post_name = wp_add_trashed_suffix_to_post_name_for_post( $post_ID );
    }
 
    $post_name = wp_unique_post_slug( $post_name, $post_ID, $post_status, $post_type, $post_parent );
 
    // Don't unslash.
    $post_mime_type = isset( $postarr['post_mime_type'] ) ? $postarr['post_mime_type'] : '';
 
    // Expected_slashed (everything!).
    $data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'post_mime_type', 'guid' );
 
    $emoji_fields = array( 'post_title', 'post_content', 'post_excerpt' );
 
    foreach ( $emoji_fields as $emoji_field ) {
        if ( isset( $data[ $emoji_field ] ) ) {
            $charset = $wpdb->get_col_charset( $wpdb->posts, $emoji_field );
            if ( 'utf8' === $charset ) {
                $data[ $emoji_field ] = wp_encode_emoji( $data[ $emoji_field ] );
            }
        }
    }
 
    if ( 'attachment' === $post_type ) {
        /**
         * Filters attachment post data before it is updated in or added to the database.
         *
         * @since 3.9.0
         *
         * @param array $data    An array of sanitized attachment post data.
         * @param array $postarr An array of unsanitized attachment post data.
         */
        $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr );
    } else {
        /**
         * Filters slashed post data just before it is inserted into the database.
         *
         * @since 2.7.0
         *
         * @param array $data    An array of slashed post data.
         * @param array $postarr An array of sanitized, but otherwise unmodified post data.
         */
        $data = apply_filters( 'wp_insert_post_data', $data, $postarr );
    }
    $data  = wp_unslash( $data );
    $where = array( 'ID' => $post_ID );
 
    if ( $update ) {
        /**
         * Fires immediately before an existing post is updated in the database.
         *
         * @since 2.5.0
         *
         * @param int   $post_ID Post ID.
         * @param array $data    Array of unslashed post data.
         */
        do_action( 'pre_post_update', $post_ID, $data );
        if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) {
            if ( $wp_error ) {
                return new WP_Error( 'db_update_error', __( 'Could not update post in the database' ), $wpdb->last_error );
            } else {
                return 0;
            }
        }
    } else {
        // If there is a suggested ID, use it if not already present.
        if ( ! empty( $import_id ) ) {
            $import_id = (int) $import_id;
            if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id ) ) ) {
                $data['ID'] = $import_id;
            }
        }
        if ( false === $wpdb->insert( $wpdb->posts, $data ) ) {
            if ( $wp_error ) {
                return new WP_Error( 'db_insert_error', __( 'Could not insert post into the database' ), $wpdb->last_error );
            } else {
                return 0;
            }
        }
        $post_ID = (int) $wpdb->insert_id;
 
        // Use the newly generated $post_ID.
        $where = array( 'ID' => $post_ID );
    }
 
    if ( empty( $data['post_name'] ) && ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
        $data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'], $post_ID ), $post_ID, $data['post_status'], $post_type, $post_parent );
        $wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where );
        clean_post_cache( $post_ID );
    }
 
    if ( is_object_in_taxonomy( $post_type, 'category' ) ) {
        wp_set_post_categories( $post_ID, $post_category );
    }
 
    if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) {
        wp_set_post_tags( $post_ID, $postarr['tags_input'] );
    }
 
    // New-style support for all custom taxonomies.
    if ( ! empty( $postarr['tax_input'] ) ) {
        foreach ( $postarr['tax_input'] as $taxonomy => $tags ) {
            $taxonomy_obj = get_taxonomy( $taxonomy );
            if ( ! $taxonomy_obj ) {
                /* translators: %s: taxonomy name */
                _doing_it_wrong( __FUNCTION__, sprintf( __( 'Invalid taxonomy: %s.' ), $taxonomy ), '4.4.0' );
                continue;
            }
 
            // array = hierarchical, string = non-hierarchical.
            if ( is_array( $tags ) ) {
                $tags = array_filter( $tags );
            }
            if ( current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
                wp_set_post_terms( $post_ID, $tags, $taxonomy );
            }
        }
    }
 
    if ( ! empty( $postarr['meta_input'] ) ) {
        foreach ( $postarr['meta_input'] as $field => $value ) {
            update_post_meta( $post_ID, $field, $value );
        }
    }
 
    $current_guid = get_post_field( 'guid', $post_ID );
 
    // Set GUID.
    if ( ! $update && '' == $current_guid ) {
        $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where );
    }
 
    if ( 'attachment' === $postarr['post_type'] ) {
        if ( ! empty( $postarr['file'] ) ) {
            update_attached_file( $post_ID, $postarr['file'] );
        }
 
        if ( ! empty( $postarr['context'] ) ) {
            add_post_meta( $post_ID, '_wp_attachment_context', $postarr['context'], true );
        }
    }
 
    // Set or remove featured image.
    if ( isset( $postarr['_thumbnail_id'] ) ) {
        $thumbnail_support = current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' ) || 'revision' === $post_type;
        if ( ! $thumbnail_support && 'attachment' === $post_type && $post_mime_type ) {
            if ( wp_attachment_is( 'audio', $post_ID ) ) {
                $thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' );
            } elseif ( wp_attachment_is( 'video', $post_ID ) ) {
                $thumbnail_support = post_type_supports( 'attachment:video', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:video' );
            }
        }
 
        if ( $thumbnail_support ) {
            $thumbnail_id = intval( $postarr['_thumbnail_id'] );
            if ( -1 === $thumbnail_id ) {
                delete_post_thumbnail( $post_ID );
            } else {
                set_post_thumbnail( $post_ID, $thumbnail_id );
            }
        }
    }
 
    clean_post_cache( $post_ID );
 
    $post = get_post( $post_ID );
 
    if ( ! empty( $postarr['page_template'] ) ) {
        $post->page_template = $postarr['page_template'];
        $page_templates      = wp_get_theme()->get_page_templates( $post );
        if ( 'default' != $postarr['page_template'] && ! isset( $page_templates[ $postarr['page_template'] ] ) ) {
            if ( $wp_error ) {
                return new WP_Error( 'invalid_page_template', __( 'Invalid page template.' ) );
            }
            update_post_meta( $post_ID, '_wp_page_template', 'default' );
        } else {
            update_post_meta( $post_ID, '_wp_page_template', $postarr['page_template'] );
        }
    }
 
    if ( 'attachment' !== $postarr['post_type'] ) {
        wp_transition_post_status( $data['post_status'], $previous_status, $post );
    } else {
        if ( $update ) {
            /**
             * Fires once an existing attachment has been updated.
             *
             * @since 2.0.0
             *
             * @param int $post_ID Attachment ID.
             */
            do_action( 'edit_attachment', $post_ID );
            $post_after = get_post( $post_ID );
 
            /**
             * Fires once an existing attachment has been updated.
             *
             * @since 4.4.0
             *
             * @param int     $post_ID      Post ID.
             * @param WP_Post $post_after   Post object following the update.
             * @param WP_Post $post_before  Post object before the update.
             */
            do_action( 'attachment_updated', $post_ID, $post_after, $post_before );
        } else {
 
            /**
             * Fires once an attachment has been added.
             *
             * @since 2.0.0
             *
             * @param int $post_ID Attachment ID.
             */
            do_action( 'add_attachment', $post_ID );
        }
 
        return $post_ID;
    }
 
    if ( $update ) {
        /**
         * Fires once an existing post has been updated.
         *
         * The dynamic portion of the hook name, `$post->post_type`, refers to
         * the post type slug.
         *
         * @since 5.1.0
         *
         * @param int     $post_ID Post ID.
         * @param WP_Post $post    Post object.
         */
        do_action( "edit_post_{$post->post_type}", $post_ID, $post );
 
        /**
         * Fires once an existing post has been updated.
         *
         * @since 1.2.0
         *
         * @param int     $post_ID Post ID.
         * @param WP_Post $post    Post object.
         */
        do_action( 'edit_post', $post_ID, $post );
 
        $post_after = get_post( $post_ID );
 
        /**
         * Fires once an existing post has been updated.
         *
         * @since 3.0.0
         *
         * @param int     $post_ID      Post ID.
         * @param WP_Post $post_after   Post object following the update.
         * @param WP_Post $post_before  Post object before the update.
         */
        do_action( 'post_updated', $post_ID, $post_after, $post_before );
    }
 
    /**
     * Fires once a post has been saved.
     *
     * The dynamic portion of the hook name, `$post->post_type`, refers to
     * the post type slug.
     *
     * @since 3.7.0
     *
     * @param int     $post_ID Post ID.
     * @param WP_Post $post    Post object.
     * @param bool    $update  Whether this is an existing post being updated or not.
     */
    do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );
 
    /**
     * Fires once a post has been saved.
     *
     * @since 1.5.0
     *
     * @param int     $post_ID Post ID.
     * @param WP_Post $post    Post object.
     * @param bool    $update  Whether this is an existing post being updated or not.
     */
    do_action( 'save_post', $post_ID, $post, $update );
 
    /**
     * Fires once a post has been saved.
     *
     * @since 2.0.0
     *
     * @param int     $post_ID Post ID.
     * @param WP_Post $post    Post object.
     * @param bool    $update  Whether this is an existing post being updated or not.
     */
    do_action( 'wp_insert_post', $post_ID, $post, $update );
 
    return $post_ID;
}

Top ↑

Changelog Changelog

Changelog
Version Description
4.4.0 A 'meta_input' array can now be passed to $postarr to add post meta data.
4.2.0 Support was added for encoding emoji in the post title, content, and excerpt.
1.0.0 Introduced.

Top ↑

More Information More Information

Top ↑

Usage Usage

1
wp_insert_post( $post$wp_error );

Top ↑

Notes Notes

  • post_title and post_content are required
  • post_status: If providing a post_status of ‘future’ you must specify the post_date in order for WordPress to know when to publish your post. See also Post Status Transitions.
  • post_category: Equivalent to calling wp_set_post_categories().
  • tags_input: Equivalent to calling wp_set_post_tags().
  • tax_input: Equivalent to calling wp_set_post_terms() for each custom taxonomy in the array. If the current user doesn’t have the capability to work with a taxonomy, then you must use wp_set_object_terms() instead.
  • page_template: If post_type is ‘page’, will attempt to set the page template. On failure, the function will return either a WP_Error or 0, and stop before the final actions are called. If the post_type is not ‘page’, the parameter is ignored. You can set the page template for a non-page by calling update_post_meta() with a key of ‘_wp_page_template’.

Top ↑

Categories Categories

Categories need to be passed as an array of integers that match the category IDs in the database. This is the case even where only one category is assigned to the post.

See also: wp_set_post_terms()

Top ↑

Security Security

wp_insert_post() passes data through sanitize_post(), which itself handles all necessary sanitization and validation (kses, etc.).

As such, you don’t need to worry about that.

You may wish, however, to remove HTML, JavaScript, and PHP tags from the post_title and any other fields. Surprisingly, WordPress does not do this automatically. This can be easily done by using the wp_strip_all_tags() function and is especially useful when building front-end post submission forms.

1
2
3
4
5
6
7
8
9
10
11
// Create post object
$my_post = array(
  'post_title'    => wp_strip_all_tags( $_POST['post_title'] ),
  'post_content'  => $_POST['post_content'],
  'post_status'   => 'publish',
  'post_author'   => 1,
  'post_category' => array( 8,39 )
);
 
// Insert the post into the database
wp_insert_post( $my_post );


Top ↑

User Contributed Notes User Contributed Notes

  1. Skip to note 1 content
    Contributed by truongwp

    Insert post with custom taxonomy and post meta data (since 4.4.0):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    $hierarchical_tax = array( 13, 10 ); // Array of tax ids.
    $non_hierarchical_terms = 'tax name 1, tax name 2'; // Can use array of ids or string of tax names separated by commas
     
    $post_arr = array(
        'post_title'   => 'Test post',
        'post_content' => 'Test post content',
        'post_status'  => 'publish',
        'post_author'  => get_current_user_id(),
        'tax_input'    => array(
            'hierarchical_tax'     => $hierarchical_tax,
            'non_hierarchical_tax' => $non_hierarchical_terms,
        ),
        'meta_input'   => array(
            'test_meta_key' => 'value of test_meta_key',
        ),
    );
  2. Skip to note 2 content
    Contributed by Codex

    Inserting a Post

    Before calling wp_insert_post() it is necessary to create an array to pass the necessary elements that make up a post. The wp_insert_post() will fill out a default list of these but the user is required to provide the title and content otherwise the database write will fail.

    The next example shows the post title, content, status, author, and post categories being set. You can add further key-value pairs, making sure the keys match the names of the columns in the wp_posts table in the database.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Gather post data.
    $my_post = array(
        'post_title'    => 'My post',
        'post_content'  => 'This is my post.',
        'post_status'   => 'publish',
        'post_author'   => 1,
        'post_category' => array( 8,39 )
    );
     
    // Insert the post into the database.
    wp_insert_post( $my_post );

    You can also get the new post ID after inserting a new post:

    1
    $post_id = wp_insert_post( $post, $wp_error );
  3. Skip to note 3 content
    Contributed by NateWr

    By default, wp_insert_post will not insert an empty post. This can cause unexpected problems if you’re passing an ID and expecting it to fall back to wp_update_post. For instance, the following will not work:

    1
    wp_insert_post( array( 'ID' => 2, 'post_parent' => 1 ) );

    You will need to call wp_update_post directly.

    1
    wp_update_post( array( 'ID' => 2, 'post_parent' => 1 ) );
  4. Skip to note 5 content
    Contributed by Compute

    When saving data containing slashes remember to add extra slashes due to wp_insert_post unslashing these values.

    1
    2
    3
    4
    5
    6
    7
    $regex = '(\b16-\d{3}\b)';
    $post_data = array(
        'post_title' => 'Test regex',
        'post_content' => $regex,
        'post_type' => 'post',
    );
    $post_id = wp_insert_post( $post_data );

    Will store (b16-d{3}b). To fix this use wp_slash():

    1
    2
    3
    4
    5
    6
    7
    $regex = '(\b16-\d{3}\b)';
    $post_data = array(
        'post_title' => 'Test regex',
        'post_content' => wp_slash( $regex ),
        'post_type' => 'post',
    );
    $post_id = wp_insert_post( $post_data );
  5. Skip to note 6 content
    Contributed by Pablo Pacheco

    As @Kiera Howe brilliantly noticed, ‘tax_input’ requires ‘assign_terms’ access.
    Depending on the case it can make its use impracticable.

    A quick solution could be replacing it by:

    1
    wp_set_object_terms( $post_id, array( $term_id1, $term_id2 ), 'tax_slug' );

    Reference:
    https://ryansechrest.com/2012/07/wordpress-taxonomy-terms-dont-insert-when-cron-job-executes-wp_insert_post/

  6. Skip to note 8 content
    Contributed by Aurovrata Venet

    Its important that you validate your post ID before you use by checking if the returned values is not a WP_error,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $args = array(
      'post_type' => 'my_custom_post',
      /*other default parameters you want to set*/
    );
    $post_id = wp_insert_post($args);
    if(!is_wp_error($post_id)){
      //the post is valid
    }else{
      //there was an error in the post insertion,
      echo $post_id->get_error_message();
    }
  7. Skip to note 9 content

    This snippet creates a post with code checking if the post already exists before creating it.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    function create_wordpress_post_with_code() {
         
            // Set the post ID to -1. This sets to no action at moment
            $post_id = -1;
         
            // Set the Author, Slug, title and content of the new post
            $author_id = 1;
            $slug = 'wordpress-post-created-with-code';
            $title = 'WordPress post created whith code';
            $content = 'This is the content of the post that we are creating right now with code.
                        More text: I motsetning til hva mange tror, er ikke Lorem Ipsum bare tilfeldig tekst.
                        Dets røtter springer helt tilbake til et stykke klassisk latinsk litteratur fra 45 år f.kr.,
                        hvilket gjør det over 2000 år gammelt. Richard McClintock - professor i latin ved Hampden-Sydney
                        College i Virginia, USA - slo opp flere av de mer obskure latinske ordene, consectetur,
                        fra en del av Lorem Ipsum, og fant dets utvilsomme opprinnelse gjennom å studere bruken
                        av disse ordene i klassisk litteratur. Lorem Ipsum kommer fra seksjon 1.10.32 og 1.10.33 i
                        "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) av Cicero, skrevet i år 45 f.kr.
                        Boken er en avhandling om teorier rundt etikk, og var veldig populær under renessansen. Den første
                        linjen av Lorem Ipsum, "Lorem Ipsum dolor sit amet...", er hentet fra en linje i seksjon 1.10.32.';
            // Cheks if doen't exists a post with slug "wordpress-post-created-with-code".
            if( !post_exists_by_slug( $slug ) ) {
                // Set the post ID
                $post_id = wp_insert_post(
                    array(
                        'comment_status'    =>   'closed',
                        'ping_status'       =>   'closed',
                        'post_author'       =>   $author_id,
                        'post_name'         =>   $slug,
                        'post_title'        =>   $title,
                        'post_content'      =>  $content,
                        'post_status'       =>   'publish',
                        'post_type'         =>   'post'
                    )
                );
            } else {
         
                    // Set pos_id to -2 becouse there is a post with this slug.
                    $post_id = -2;
             
            } // end if
         
        } // end oaf_create_post_with_code
     add_filter( 'after_setup_theme', 'create_wordpress_post_with_code' );
     /**
     * post_exists_by_slug.
     *
     * @return mixed boolean false if no post exists; post ID otherwise.
     */
    function post_exists_by_slug( $post_slug ) {
        $args_posts = array(
            'post_type'      => 'post',
            'post_status'    => 'any',
            'name'           => $post_slug,
            'posts_per_page' => 1,
        );
        $loop_posts = new WP_Query( $args_posts );
        if ( ! $loop_posts->have_posts() ) {
            return false;
        } else {
            $loop_posts->the_post();
            return $loop_posts->post->ID;
        }
    }

You must log in before being able to contribute a note or feedback.