基础代码

This commit is contained in:
2021-02-26 22:23:13 +08:00
parent 7884df52f0
commit a719feebba
2585 changed files with 328263 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
{
"extends": [
"plugin:@wordpress/eslint-plugin/esnext"
],
"env": {
"browser": true
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"extends": [
"stylelint-config-wordpress/scss"
],
"ignoreFiles": [
"sass/_normalize.scss"
],
"rules": {
"font-family-no-missing-generic-family-keyword": null,
"no-descending-specificity": null,
"block-no-empty": null,
"no-duplicate-selectors": null,
"font-family-no-duplicate-names": null,
"selector-class-pattern": null
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
/**
* The template for displaying 404 pages (not found)
*
* @link https://codex.wordpress.org/Creating_an_Error_404_Page
*
* @package CyyWordpress
*/
get_header();
?>
<main id="primary" class="site-main">
<section class="error-404 not-found">
<header class="page-header">
<h1 class="page-title"><?php esc_html_e( 'Oops! That page can&rsquo;t be found.', 'cyywordpress' ); ?></h1>
</header><!-- .page-header -->
<div class="page-content">
<p><?php esc_html_e( 'It looks like nothing was found at this location. Maybe try one of the links below or a search?', 'cyywordpress' ); ?></p>
<?php
get_search_form();
the_widget( 'WP_Widget_Recent_Posts' );
?>
<div class="widget widget_categories">
<h2 class="widget-title"><?php esc_html_e( 'Most Used Categories', 'cyywordpress' ); ?></h2>
<ul>
<?php
wp_list_categories(
array(
'orderby' => 'count',
'order' => 'DESC',
'show_count' => 1,
'title_li' => '',
'number' => 10,
)
);
?>
</ul>
</div><!-- .widget -->
<?php
/* translators: %1$s: smiley */
$cyywordpress_archive_content = '<p>' . sprintf( esc_html__( 'Try looking in the monthly archives. %1$s', 'cyywordpress' ), convert_smilies( ':)' ) ) . '</p>';
the_widget( 'WP_Widget_Archives', 'dropdown=1', "after_title=</h2>$cyywordpress_archive_content" );
the_widget( 'WP_Widget_Tag_Cloud' );
?>
</div><!-- .page-content -->
</section><!-- .error-404 -->
</main><!-- #main -->
<?php
get_footer();
+51
View File
@@ -0,0 +1,51 @@
<?php
/**
* The template for displaying archive pages
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
get_header();
?>
<main id="primary" class="site-main">
<?php if ( have_posts() ) : ?>
<header class="page-header">
<?php
the_archive_title( '<h1 class="page-title">', '</h1>' );
the_archive_description( '<div class="archive-description">', '</div>' );
?>
</header><!-- .page-header -->
<?php
/* Start the Loop */
while ( have_posts() ) :
the_post();
/*
* Include the Post-Type-specific template for the content.
* If you want to override this in a child theme, then include a file
* called content-___.php (where ___ is the Post Type name) and that will be used instead.
*/
get_template_part( 'template-parts/content', get_post_type() );
endwhile;
the_posts_navigation();
else :
get_template_part( 'template-parts/content', 'none' );
endif;
?>
</main><!-- #main -->
<?php
get_sidebar();
get_footer();
+77
View File
@@ -0,0 +1,77 @@
<?php
/**
* The template for displaying comments
*
* This is the template that displays the area of the page that contains both the current comments
* and the comment form.
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
/*
* If the current post is protected by a password and
* the visitor has not yet entered the password we will
* return early without loading the comments.
*/
if ( post_password_required() ) {
return;
}
?>
<div id="comments" class="comments-area">
<?php
// You can start editing here -- including this comment!
if ( have_comments() ) :
?>
<h2 class="comments-title">
<?php
$cyywordpress_comment_count = get_comments_number();
if ( '1' === $cyywordpress_comment_count ) {
printf(
/* translators: 1: title. */
esc_html__( 'One thought on &ldquo;%1$s&rdquo;', 'cyywordpress' ),
'<span>' . wp_kses_post( get_the_title() ) . '</span>'
);
} else {
printf(
/* translators: 1: comment count number, 2: title. */
esc_html( _nx( '%1$s thought on &ldquo;%2$s&rdquo;', '%1$s thoughts on &ldquo;%2$s&rdquo;', $cyywordpress_comment_count, 'comments title', 'cyywordpress' ) ),
number_format_i18n( $cyywordpress_comment_count ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'<span>' . wp_kses_post( get_the_title() ) . '</span>'
);
}
?>
</h2><!-- .comments-title -->
<?php the_comments_navigation(); ?>
<ol class="comment-list">
<?php
wp_list_comments(
array(
'style' => 'ol',
'short_ping' => true,
)
);
?>
</ol><!-- .comment-list -->
<?php
the_comments_navigation();
// If comments are closed and there are comments, let's leave a little note, shall we?
if ( ! comments_open() ) :
?>
<p class="no-comments"><?php esc_html_e( 'Comments are closed.', 'cyywordpress' ); ?></p>
<?php
endif;
endif; // Check for have_comments().
comment_form();
?>
</div><!-- #comments -->
+35
View File
@@ -0,0 +1,35 @@
{
"name": "automattic/underscores",
"type": "wordpress-theme",
"description": "Hi. I'm a starter theme called _s, or underscores, if you like. I'm a theme meant for hacking so don't use me as a Parent Theme. Instead try turning me into the next, most awesome, WordPress theme out there. That's what I'm here for.",
"keywords": [
"WordPress",
"Themes"
],
"homepage": "https://github.com/Automattic/_s",
"license": "GPL-2.0-or-later",
"authors": [
{
"name": "Contributors",
"homepage": "https://github.com/Automattic/_s/graphs/contributors"
}
],
"require": {
"php": ">=5.6"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"wptrt/wpthemereview": "^0.2.1",
"php-parallel-lint/php-parallel-lint": "^1.2.0",
"wp-cli/i18n-command": "^2.2.5"
},
"scripts": {
"lint:wpcs": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs",
"lint:php": "@php ./vendor/bin/parallel-lint --exclude .git --exclude vendor .",
"make-pot": "wp i18n make-pot . languages/_s.pot"
},
"support": {
"issues": "https://github.com/Automattic/_s/issues",
"source": "https://github.com/Automattic/_s"
}
}
Generated
+1079
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
<?php
/**
* The template for displaying the footer
*
* Contains the closing of the #content div and all content after.
*
* @link https://developer.wordpress.org/themes/basics/template-files/#template-partials
*
* @package CyyWordpress
*/
?>
<footer id="colophon" class="site-footer">
<div class="site-info">
<a href="<?php echo esc_url( __( 'https://wordpress.org/', 'cyywordpress' ) ); ?>">
<?php
/* translators: %s: CMS name, i.e. WordPress. */
printf( esc_html__( 'Proudly powered by %s', 'cyywordpress' ), 'WordPress' );
?>
</a>
<span class="sep"> | </span>
<?php
/* translators: 1: Theme name, 2: Theme author. */
printf( esc_html__( 'Theme: %1$s by %2$s.', 'cyywordpress' ), 'cyywordpress', '<a href="http://underscores.me/">Underscores.me</a>' );
?>
</div><!-- .site-info -->
</footer><!-- #colophon -->
</div><!-- #page -->
<?php wp_footer(); ?>
</body>
</html>
+180
View File
@@ -0,0 +1,180 @@
<?php
/**
* CyyWordpress functions and definitions
*
* @link https://developer.wordpress.org/themes/basics/theme-functions/
*
* @package CyyWordpress
*/
if ( ! defined( '_S_VERSION' ) ) {
// Replace the version number of the theme on each release.
define( '_S_VERSION', '1.0.0' );
}
if ( ! function_exists( 'cyywordpress_setup' ) ) :
/**
* Sets up theme defaults and registers support for various WordPress features.
*
* Note that this function is hooked into the after_setup_theme hook, which
* runs before the init hook. The init hook is too late for some features, such
* as indicating support for post thumbnails.
*/
function cyywordpress_setup() {
/*
* Make theme available for translation.
* Translations can be filed in the /languages/ directory.
* If you're building a theme based on CyyWordpress, use a find and replace
* to change 'cyywordpress' to the name of your theme in all the template files.
*/
load_theme_textdomain( 'cyywordpress', get_template_directory() . '/languages' );
// Add default posts and comments RSS feed links to head.
add_theme_support( 'automatic-feed-links' );
/*
* Let WordPress manage the document title.
* By adding theme support, we declare that this theme does not use a
* hard-coded <title> tag in the document head, and expect WordPress to
* provide it for us.
*/
add_theme_support( 'title-tag' );
/*
* Enable support for Post Thumbnails on posts and pages.
*
* @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/
*/
add_theme_support( 'post-thumbnails' );
// This theme uses wp_nav_menu() in one location.
register_nav_menus(
array(
'menu-1' => esc_html__( 'Primary', 'cyywordpress' ),
)
);
/*
* Switch default core markup for search form, comment form, and comments
* to output valid HTML5.
*/
add_theme_support(
'html5',
array(
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
'style',
'script',
)
);
// Set up the WordPress core custom background feature.
add_theme_support(
'custom-background',
apply_filters(
'cyywordpress_custom_background_args',
array(
'default-color' => 'ffffff',
'default-image' => '',
)
)
);
// Add theme support for selective refresh for widgets.
add_theme_support( 'customize-selective-refresh-widgets' );
/**
* Add support for core custom logo.
*
* @link https://codex.wordpress.org/Theme_Logo
*/
add_theme_support(
'custom-logo',
array(
'height' => 250,
'width' => 250,
'flex-width' => true,
'flex-height' => true,
)
);
}
endif;
add_action( 'after_setup_theme', 'cyywordpress_setup' );
/**
* Set the content width in pixels, based on the theme's design and stylesheet.
*
* Priority 0 to make it available to lower priority callbacks.
*
* @global int $content_width
*/
function cyywordpress_content_width() {
$GLOBALS['content_width'] = apply_filters( 'cyywordpress_content_width', 640 );
}
add_action( 'after_setup_theme', 'cyywordpress_content_width', 0 );
/**
* Register widget area.
*
* @link https://developer.wordpress.org/themes/functionality/sidebars/#registering-a-sidebar
*/
function cyywordpress_widgets_init() {
register_sidebar(
array(
'name' => esc_html__( 'Sidebar', 'cyywordpress' ),
'id' => 'sidebar-1',
'description' => esc_html__( 'Add widgets here.', 'cyywordpress' ),
'before_widget' => '<section id="%1$s" class="widget %2$s">',
'after_widget' => '</section>',
'before_title' => '<h2 class="widget-title">',
'after_title' => '</h2>',
)
);
}
add_action( 'widgets_init', 'cyywordpress_widgets_init' );
/**
* Enqueue scripts and styles.
*/
function cyywordpress_scripts() {
wp_enqueue_style( 'cyywordpress-style', get_stylesheet_uri(), array(), _S_VERSION );
wp_style_add_data( 'cyywordpress-style', 'rtl', 'replace' );
wp_enqueue_script( 'cyywordpress-navigation', get_template_directory_uri() . '/js/navigation.js', array(), _S_VERSION, true );
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
wp_enqueue_script( 'comment-reply' );
}
}
add_action( 'wp_enqueue_scripts', 'cyywordpress_scripts' );
/**
* Implement the Custom Header feature.
*/
require get_template_directory() . '/inc/custom-header.php';
/**
* Custom template tags for this theme.
*/
require get_template_directory() . '/inc/template-tags.php';
/**
* Functions which enhance the theme by hooking into WordPress.
*/
require get_template_directory() . '/inc/template-functions.php';
/**
* Customizer additions.
*/
require get_template_directory() . '/inc/customizer.php';
/**
* Load Jetpack compatibility file.
*/
if ( defined( 'JETPACK__VERSION' ) ) {
require get_template_directory() . '/inc/jetpack.php';
}
+59
View File
@@ -0,0 +1,59 @@
<?php
/**
* The header for our theme
*
* This is the template that displays all of the <head> section and everything up until <div id="content">
*
* @link https://developer.wordpress.org/themes/basics/template-files/#template-partials
*
* @package CyyWordpress
*/
?>
<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="profile" href="https://gmpg.org/xfn/11">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<div id="page" class="site">
<a class="skip-link screen-reader-text" href="#primary"><?php esc_html_e( 'Skip to content', 'cyywordpress' ); ?></a>
<header id="masthead" class="site-header">
<div class="site-branding">
<?php
the_custom_logo();
if ( is_front_page() && is_home() ) :
?>
<h1 class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></h1>
<?php
else :
?>
<p class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></p>
<?php
endif;
$cyywordpress_description = get_bloginfo( 'description', 'display' );
if ( $cyywordpress_description || is_customize_preview() ) :
?>
<p class="site-description"><?php echo $cyywordpress_description; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></p>
<?php endif; ?>
</div><!-- .site-branding -->
<nav id="site-navigation" class="main-navigation">
<button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false"><?php esc_html_e( 'Primary Menu', 'cyywordpress' ); ?></button>
<?php
wp_nav_menu(
array(
'theme_location' => 'menu-1',
'menu_id' => 'primary-menu',
)
);
?>
</nav><!-- #site-navigation -->
</header><!-- #masthead -->
+78
View File
@@ -0,0 +1,78 @@
<?php
/**
* Sample implementation of the Custom Header feature
*
* You can add an optional custom header image to header.php like so ...
*
<?php the_header_image_tag(); ?>
*
* @link https://developer.wordpress.org/themes/functionality/custom-headers/
*
* @package CyyWordpress
*/
/**
* Set up the WordPress core custom header feature.
*
* @uses cyywordpress_header_style()
*/
function cyywordpress_custom_header_setup() {
add_theme_support(
'custom-header',
apply_filters(
'cyywordpress_custom_header_args',
array(
'default-image' => '',
'default-text-color' => '000000',
'width' => 1000,
'height' => 250,
'flex-height' => true,
'wp-head-callback' => 'cyywordpress_header_style',
)
)
);
}
add_action( 'after_setup_theme', 'cyywordpress_custom_header_setup' );
if ( ! function_exists( 'cyywordpress_header_style' ) ) :
/**
* Styles the header image and text displayed on the blog.
*
* @see cyywordpress_custom_header_setup().
*/
function cyywordpress_header_style() {
$header_text_color = get_header_textcolor();
/*
* If no custom options for text are set, let's bail.
* get_header_textcolor() options: Any hex value, 'blank' to hide text. Default: add_theme_support( 'custom-header' ).
*/
if ( get_theme_support( 'custom-header', 'default-text-color' ) === $header_text_color ) {
return;
}
// If we get this far, we have custom styles. Let's do this.
?>
<style type="text/css">
<?php
// Has the text been hidden?
if ( ! display_header_text() ) :
?>
.site-title,
.site-description {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
}
<?php
// If the user has set a custom color for the text use that.
else :
?>
.site-title a,
.site-description {
color: #<?php echo esc_attr( $header_text_color ); ?>;
}
<?php endif; ?>
</style>
<?php
}
endif;
+61
View File
@@ -0,0 +1,61 @@
<?php
/**
* CyyWordpress Theme Customizer
*
* @package CyyWordpress
*/
/**
* Add postMessage support for site title and description for the Theme Customizer.
*
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
*/
function cyywordpress_customize_register( $wp_customize ) {
$wp_customize->get_setting( 'blogname' )->transport = 'postMessage';
$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';
$wp_customize->get_setting( 'header_textcolor' )->transport = 'postMessage';
if ( isset( $wp_customize->selective_refresh ) ) {
$wp_customize->selective_refresh->add_partial(
'blogname',
array(
'selector' => '.site-title a',
'render_callback' => 'cyywordpress_customize_partial_blogname',
)
);
$wp_customize->selective_refresh->add_partial(
'blogdescription',
array(
'selector' => '.site-description',
'render_callback' => 'cyywordpress_customize_partial_blogdescription',
)
);
}
}
add_action( 'customize_register', 'cyywordpress_customize_register' );
/**
* Render the site title for the selective refresh partial.
*
* @return void
*/
function cyywordpress_customize_partial_blogname() {
bloginfo( 'name' );
}
/**
* Render the site tagline for the selective refresh partial.
*
* @return void
*/
function cyywordpress_customize_partial_blogdescription() {
bloginfo( 'description' );
}
/**
* Binds JS handlers to make Theme Customizer preview reload changes asynchronously.
*/
function cyywordpress_customize_preview_js() {
wp_enqueue_script( 'cyywordpress-customizer', get_template_directory_uri() . '/js/customizer.js', array( 'customize-preview' ), _S_VERSION, true );
}
add_action( 'customize_preview_init', 'cyywordpress_customize_preview_js' );
+65
View File
@@ -0,0 +1,65 @@
<?php
/**
* Jetpack Compatibility File
*
* @link https://jetpack.com/
*
* @package CyyWordpress
*/
/**
* Jetpack setup function.
*
* See: https://jetpack.com/support/infinite-scroll/
* See: https://jetpack.com/support/responsive-videos/
* See: https://jetpack.com/support/content-options/
*/
function cyywordpress_jetpack_setup() {
// Add theme support for Infinite Scroll.
add_theme_support(
'infinite-scroll',
array(
'container' => 'main',
'render' => 'cyywordpress_infinite_scroll_render',
'footer' => 'page',
)
);
// Add theme support for Responsive Videos.
add_theme_support( 'jetpack-responsive-videos' );
// Add theme support for Content Options.
add_theme_support(
'jetpack-content-options',
array(
'post-details' => array(
'stylesheet' => 'cyywordpress-style',
'date' => '.posted-on',
'categories' => '.cat-links',
'tags' => '.tags-links',
'author' => '.byline',
'comment' => '.comments-link',
),
'featured-images' => array(
'archive' => true,
'post' => true,
'page' => true,
),
)
);
}
add_action( 'after_setup_theme', 'cyywordpress_jetpack_setup' );
/**
* Custom render function for Infinite Scroll.
*/
function cyywordpress_infinite_scroll_render() {
while ( have_posts() ) {
the_post();
if ( is_search() ) :
get_template_part( 'template-parts/content', 'search' );
else :
get_template_part( 'template-parts/content', get_post_type() );
endif;
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
/**
* Functions which enhance the theme by hooking into WordPress
*
* @package CyyWordpress
*/
/**
* Adds custom classes to the array of body classes.
*
* @param array $classes Classes for the body element.
* @return array
*/
function cyywordpress_body_classes( $classes ) {
// Adds a class of hfeed to non-singular pages.
if ( ! is_singular() ) {
$classes[] = 'hfeed';
}
// Adds a class of no-sidebar when there is no sidebar present.
if ( ! is_active_sidebar( 'sidebar-1' ) ) {
$classes[] = 'no-sidebar';
}
return $classes;
}
add_filter( 'body_class', 'cyywordpress_body_classes' );
/**
* Add a pingback url auto-discovery header for single posts, pages, or attachments.
*/
function cyywordpress_pingback_header() {
if ( is_singular() && pings_open() ) {
printf( '<link rel="pingback" href="%s">', esc_url( get_bloginfo( 'pingback_url' ) ) );
}
}
add_action( 'wp_head', 'cyywordpress_pingback_header' );
+165
View File
@@ -0,0 +1,165 @@
<?php
/**
* Custom template tags for this theme
*
* Eventually, some of the functionality here could be replaced by core features.
*
* @package CyyWordpress
*/
if ( ! function_exists( 'cyywordpress_posted_on' ) ) :
/**
* Prints HTML with meta information for the current post-date/time.
*/
function cyywordpress_posted_on() {
$time_string = '<time class="entry-date published updated" datetime="%1$s">%2$s</time>';
if ( get_the_time( 'U' ) !== get_the_modified_time( 'U' ) ) {
$time_string = '<time class="entry-date published" datetime="%1$s">%2$s</time><time class="updated" datetime="%3$s">%4$s</time>';
}
$time_string = sprintf(
$time_string,
esc_attr( get_the_date( DATE_W3C ) ),
esc_html( get_the_date() ),
esc_attr( get_the_modified_date( DATE_W3C ) ),
esc_html( get_the_modified_date() )
);
$posted_on = sprintf(
/* translators: %s: post date. */
esc_html_x( 'Posted on %s', 'post date', 'cyywordpress' ),
'<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>'
);
echo '<span class="posted-on">' . $posted_on . '</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
endif;
if ( ! function_exists( 'cyywordpress_posted_by' ) ) :
/**
* Prints HTML with meta information for the current author.
*/
function cyywordpress_posted_by() {
$byline = sprintf(
/* translators: %s: post author. */
esc_html_x( 'by %s', 'post author', 'cyywordpress' ),
'<span class="author vcard"><a class="url fn n" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a></span>'
);
echo '<span class="byline"> ' . $byline . '</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
endif;
if ( ! function_exists( 'cyywordpress_entry_footer' ) ) :
/**
* Prints HTML with meta information for the categories, tags and comments.
*/
function cyywordpress_entry_footer() {
// Hide category and tag text for pages.
if ( 'post' === get_post_type() ) {
/* translators: used between list items, there is a space after the comma */
$categories_list = get_the_category_list( esc_html__( ', ', 'cyywordpress' ) );
if ( $categories_list ) {
/* translators: 1: list of categories. */
printf( '<span class="cat-links">' . esc_html__( 'Posted in %1$s', 'cyywordpress' ) . '</span>', $categories_list ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/* translators: used between list items, there is a space after the comma */
$tags_list = get_the_tag_list( '', esc_html_x( ', ', 'list item separator', 'cyywordpress' ) );
if ( $tags_list ) {
/* translators: 1: list of tags. */
printf( '<span class="tags-links">' . esc_html__( 'Tagged %1$s', 'cyywordpress' ) . '</span>', $tags_list ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
if ( ! is_single() && ! post_password_required() && ( comments_open() || get_comments_number() ) ) {
echo '<span class="comments-link">';
comments_popup_link(
sprintf(
wp_kses(
/* translators: %s: post title */
__( 'Leave a Comment<span class="screen-reader-text"> on %s</span>', 'cyywordpress' ),
array(
'span' => array(
'class' => array(),
),
)
),
wp_kses_post( get_the_title() )
)
);
echo '</span>';
}
edit_post_link(
sprintf(
wp_kses(
/* translators: %s: Name of current post. Only visible to screen readers */
__( 'Edit <span class="screen-reader-text">%s</span>', 'cyywordpress' ),
array(
'span' => array(
'class' => array(),
),
)
),
wp_kses_post( get_the_title() )
),
'<span class="edit-link">',
'</span>'
);
}
endif;
if ( ! function_exists( 'cyywordpress_post_thumbnail' ) ) :
/**
* Displays an optional post thumbnail.
*
* Wraps the post thumbnail in an anchor element on index views, or a div
* element when on single views.
*/
function cyywordpress_post_thumbnail() {
if ( post_password_required() || is_attachment() || ! has_post_thumbnail() ) {
return;
}
if ( is_singular() ) :
?>
<div class="post-thumbnail">
<?php the_post_thumbnail(); ?>
</div><!-- .post-thumbnail -->
<?php else : ?>
<a class="post-thumbnail" href="<?php the_permalink(); ?>" aria-hidden="true" tabindex="-1">
<?php
the_post_thumbnail(
'post-thumbnail',
array(
'alt' => the_title_attribute(
array(
'echo' => false,
)
),
)
);
?>
</a>
<?php
endif; // End is_singular().
}
endif;
if ( ! function_exists( 'wp_body_open' ) ) :
/**
* Shim for sites older than 5.2.
*
* @link https://core.trac.wordpress.org/ticket/12563
*/
function wp_body_open() {
do_action( 'wp_body_open' );
}
endif;
+57
View File
@@ -0,0 +1,57 @@
<?php
/**
* The main template file
*
* This is the most generic template file in a WordPress theme
* and one of the two required files for a theme (the other being style.css).
* It is used to display a page when nothing more specific matches a query.
* E.g., it puts together the home page when no home.php file exists.
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
get_header();
?>
<main id="primary" class="site-main">
<?php
if ( have_posts() ) :
if ( is_home() && ! is_front_page() ) :
?>
<header>
<h1 class="page-title screen-reader-text"><?php single_post_title(); ?></h1>
</header>
<?php
endif;
/* Start the Loop */
while ( have_posts() ) :
the_post();
/*
* Include the Post-Type-specific template for the content.
* If you want to override this in a child theme, then include a file
* called content-___.php (where ___ is the Post Type name) and that will be used instead.
*/
get_template_part( 'template-parts/content', get_post_type() );
endwhile;
the_posts_navigation();
else :
get_template_part( 'template-parts/content', 'none' );
endif;
?>
</main><!-- #main -->
<?php
get_sidebar();
get_footer();
+42
View File
@@ -0,0 +1,42 @@
/* global wp, jQuery */
/**
* File customizer.js.
*
* Theme Customizer enhancements for a better user experience.
*
* Contains handlers to make Theme Customizer preview reload changes asynchronously.
*/
( function( $ ) {
// Site title and description.
wp.customize( 'blogname', function( value ) {
value.bind( function( to ) {
$( '.site-title a' ).text( to );
} );
} );
wp.customize( 'blogdescription', function( value ) {
value.bind( function( to ) {
$( '.site-description' ).text( to );
} );
} );
// Header text color.
wp.customize( 'header_textcolor', function( value ) {
value.bind( function( to ) {
if ( 'blank' === to ) {
$( '.site-title, .site-description' ).css( {
clip: 'rect(1px, 1px, 1px, 1px)',
position: 'absolute',
} );
} else {
$( '.site-title, .site-description' ).css( {
clip: 'auto',
position: 'relative',
} );
$( '.site-title a, .site-description' ).css( {
color: to,
} );
}
} );
} );
}( jQuery ) );
+99
View File
@@ -0,0 +1,99 @@
/**
* File navigation.js.
*
* Handles toggling the navigation menu for small screens and enables TAB key
* navigation support for dropdown menus.
*/
( function() {
const siteNavigation = document.getElementById( 'site-navigation' );
// Return early if the navigation don't exist.
if ( ! siteNavigation ) {
return;
}
const button = siteNavigation.getElementsByTagName( 'button' )[ 0 ];
// Return early if the button don't exist.
if ( 'undefined' === typeof button ) {
return;
}
const menu = siteNavigation.getElementsByTagName( 'ul' )[ 0 ];
// Hide menu toggle button if menu is empty and return early.
if ( 'undefined' === typeof menu ) {
button.style.display = 'none';
return;
}
if ( ! menu.classList.contains( 'nav-menu' ) ) {
menu.classList.add( 'nav-menu' );
}
// Toggle the .toggled class and the aria-expanded value each time the button is clicked.
button.addEventListener( 'click', function() {
siteNavigation.classList.toggle( 'toggled' );
if ( button.getAttribute( 'aria-expanded' ) === 'true' ) {
button.setAttribute( 'aria-expanded', 'false' );
} else {
button.setAttribute( 'aria-expanded', 'true' );
}
} );
// Remove the .toggled class and set aria-expanded to false when the user clicks outside the navigation.
document.addEventListener( 'click', function( event ) {
const isClickInside = siteNavigation.contains( event.target );
if ( ! isClickInside ) {
siteNavigation.classList.remove( 'toggled' );
button.setAttribute( 'aria-expanded', 'false' );
}
} );
// Get all the link elements within the menu.
const links = menu.getElementsByTagName( 'a' );
// Get all the link elements with children within the menu.
const linksWithChildren = menu.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' );
// Toggle focus each time a menu link is focused or blurred.
for ( const link of links ) {
link.addEventListener( 'focus', toggleFocus, true );
link.addEventListener( 'blur', toggleFocus, true );
}
// Toggle focus each time a menu link with children receive a touch event.
for ( const link of linksWithChildren ) {
link.addEventListener( 'touchstart', toggleFocus, false );
}
/**
* Sets or removes .focus class on an element.
*/
function toggleFocus() {
if ( event.type === 'focus' || event.type === 'blur' ) {
let self = this;
// Move up through the ancestors of the current link until we hit .nav-menu.
while ( ! self.classList.contains( 'nav-menu' ) ) {
// On li elements toggle the class .focus.
if ( 'li' === self.tagName.toLowerCase() ) {
self.classList.toggle( 'focus' );
}
self = self.parentNode;
}
}
if ( event.type === 'touchstart' ) {
const menuItem = this.parentNode;
event.preventDefault();
for ( const link of menuItem.parentNode.children ) {
if ( menuItem !== link ) {
link.classList.remove( 'focus' );
}
}
menuItem.classList.toggle( 'focus' );
}
}
}() );
+198
View File
@@ -0,0 +1,198 @@
# Copyright (C) 2020 Automattic
# This file is distributed under the GNU General Public License v2 or later.
msgid ""
msgstr ""
"Project-Id-Version: _s 1.0.0\n"
"Report-Msgid-Bugs-To: https://wordpress.org/support/theme/_s\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2020-04-17T21:03:15+00:00\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"X-Generator: WP-CLI 2.4.0\n"
"X-Domain: _s\n"
#. Theme Name of the theme
msgid "_s"
msgstr ""
#. Theme URI of the theme
msgid "https://underscores.me/"
msgstr ""
#. Description of the theme
msgid "Hi. I'm a starter theme called <code>_s</code>, or <em>underscores</em>, if you like. I'm a theme meant for hacking so don't use me as a <em>Parent Theme</em>. Instead try turning me into the next, most awesome, WordPress theme out there. That's what I'm here for."
msgstr ""
#. Author of the theme
msgid "Automattic"
msgstr ""
#. Author URI of the theme
msgid "https://automattic.com/"
msgstr ""
#: 404.php:18
msgid "Oops! That page can&rsquo;t be found."
msgstr ""
#: 404.php:22
msgid "It looks like nothing was found at this location. Maybe try one of the links below or a search?"
msgstr ""
#: 404.php:31
msgid "Most Used Categories"
msgstr ""
#. translators: %1$s: smiley
#: 404.php:49
msgid "Try looking in the monthly archives. %1$s"
msgstr ""
#. translators: 1: title.
#: comments.php:35
msgid "One thought on &ldquo;%1$s&rdquo;"
msgstr ""
#. translators: 1: comment count number, 2: title.
#: comments.php:41
msgctxt "comments title"
msgid "%1$s thought on &ldquo;%2$s&rdquo;"
msgid_plural "%1$s thoughts on &ldquo;%2$s&rdquo;"
msgstr[0] ""
msgstr[1] ""
#: comments.php:68
msgid "Comments are closed."
msgstr ""
#: footer.php:18
msgid "https://wordpress.org/"
msgstr ""
#. translators: %s: CMS name, i.e. WordPress.
#: footer.php:21
msgid "Proudly powered by %s"
msgstr ""
#. translators: 1: Theme name, 2: Theme author.
#: footer.php:27
msgid "Theme: %1$s by %2$s."
msgstr ""
#: functions.php:53
msgid "Primary"
msgstr ""
#: functions.php:130
msgid "Sidebar"
msgstr ""
#: functions.php:132
msgid "Add widgets here."
msgstr ""
#: header.php:26
msgid "Skip to content"
msgstr ""
#: header.php:49
msgid "Primary Menu"
msgstr ""
#. translators: %s: post date.
#: inc/template-tags.php:30
msgctxt "post date"
msgid "Posted on %s"
msgstr ""
#. translators: %s: post author.
#: inc/template-tags.php:46
msgctxt "post author"
msgid "by %s"
msgstr ""
#. translators: used between list items, there is a space after the comma
#: inc/template-tags.php:63
msgid ", "
msgstr ""
#. translators: 1: list of categories.
#: inc/template-tags.php:66
msgid "Posted in %1$s"
msgstr ""
#. translators: used between list items, there is a space after the comma
#: inc/template-tags.php:70
msgctxt "list item separator"
msgid ", "
msgstr ""
#. translators: 1: list of tags.
#: inc/template-tags.php:73
msgid "Tagged %1$s"
msgstr ""
#. translators: %s: post title
#: inc/template-tags.php:83
msgid "Leave a Comment<span class=\"screen-reader-text\"> on %s</span>"
msgstr ""
#. translators: %s: Name of current post. Only visible to screen readers
#: inc/template-tags.php:100
#: template-parts/content-page.php:39
msgid "Edit <span class=\"screen-reader-text\">%s</span>"
msgstr ""
#: inc/woocommerce.php:186
msgid "View your shopping cart"
msgstr ""
#. translators: number of items in the mini cart.
#: inc/woocommerce.php:190
msgid "%d item"
msgid_plural "%d items"
msgstr[0] ""
msgstr[1] ""
#. translators: %s: search query.
#: search.php:22
msgid "Search Results for: %s"
msgstr ""
#: single.php:23
msgid "Previous:"
msgstr ""
#: single.php:24
msgid "Next:"
msgstr ""
#: template-parts/content-none.php:14
msgid "Nothing Found"
msgstr ""
#. translators: 1: link to WP admin new post page.
#: template-parts/content-none.php:24
msgid "Ready to publish your first post? <a href=\"%1$s\">Get started here</a>."
msgstr ""
#: template-parts/content-none.php:37
msgid "Sorry, but nothing matched your search terms. Please try again with some different keywords."
msgstr ""
#: template-parts/content-none.php:44
msgid "It seems we can&rsquo;t find what you&rsquo;re looking for. Perhaps searching can help."
msgstr ""
#: template-parts/content-page.php:25
#: template-parts/content.php:53
msgid "Pages:"
msgstr ""
#. translators: %s: Name of current post. Only visible to screen readers
#: template-parts/content.php:40
msgid "Continue reading<span class=\"screen-reader-text\"> \"%s\"</span>"
msgstr ""
+7
View File
@@ -0,0 +1,7 @@
Place your theme language files in this directory.
Please visit the following links to learn more about translating WordPress themes:
https://make.wordpress.org/polyglots/teams/
https://developer.wordpress.org/themes/functionality/localization/
https://developer.wordpress.org/reference/functions/load_theme_textdomain/
+46
View File
@@ -0,0 +1,46 @@
{
"name": "underscores",
"version": "1.0.0",
"description": "Hi. I'm a starter theme called _s, or underscores, if you like. I'm a theme meant for hacking so don't use me as a Parent Theme. Instead try turning me into the next, most awesome, WordPress theme out there. That's what I'm here for.",
"author": "Automattic Theme Team",
"license": "GPL-2.0-or-later",
"keywords": [
"WordPress",
"Theme"
],
"homepage": "https://github.com/Automattic/_s#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/Automattic/_s.git"
},
"bugs": {
"url": "https://github.com/Automattic/_s/issues"
},
"devDependencies": {
"@wordpress/scripts": "^12.1.0",
"dir-archiver": "^1.1.1",
"node-sass": "^4.14.1",
"rtlcss": "^2.5.0"
},
"rtlcssConfig": {
"options": {
"autoRename": false,
"autoRenameStrict": false,
"blacklist": {},
"clean": true,
"greedy": false,
"processUrls": false,
"stringMap": []
},
"plugins": [],
"map": false
},
"scripts": {
"watch": "node-sass sass/ -o ./ --source-map true --output-style expanded --indent-type tab --indent-width 1 -w",
"compile:css": "node-sass sass/ -o ./ && stylelint '*.css' --fix || true && stylelint '*.css' --fix",
"compile:rtl": "rtlcss style.css style-rtl.css",
"lint:scss": "wp-scripts lint-style 'sass/**/*.scss'",
"lint:js": "wp-scripts lint-js 'js/*.js'",
"bundle": "dir-archiver --src . --dest ../_s.zip --exclude .DS_Store .stylelintrc.json .eslintrc .git .gitattributes .github .gitignore README.md composer.json composer.lock node_modules vendor package-lock.json package.json .travis.yml phpcs.xml.dist sass style.css.map"
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* The template for displaying all pages
*
* This is the template that displays all pages by default.
* Please note that this is the WordPress construct of pages
* and that other 'pages' on your WordPress site may use a
* different template.
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
get_header();
?>
<main id="primary" class="site-main">
<?php
while ( have_posts() ) :
the_post();
get_template_part( 'template-parts/content', 'page' );
// If comments are open or we have at least one comment, load up the comment template.
if ( comments_open() || get_comments_number() ) :
comments_template();
endif;
endwhile; // End of the loop.
?>
</main><!-- #main -->
<?php
get_sidebar();
get_footer();
+110
View File
@@ -0,0 +1,110 @@
<?xml version="1.0"?>
<ruleset name="WordPress Theme Coding Standards">
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml -->
<!-- See https://github.com/WordPress/WordPress-Coding-Standards -->
<!-- See https://github.com/WPTRT/WPThemeReview -->
<!-- See https://github.com/PHPCompatibility/PHPCompatibilityWP -->
<!-- Set a description for this ruleset. -->
<description>A custom set of code standard rules to check for WordPress themes.</description>
<!--
#############################################################################
COMMAND LINE ARGUMENTS
https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml
#############################################################################
-->
<!-- Pass some flags to PHPCS:
p flag: Show progress of the run.
s flag: Show sniff codes in all reports.
-->
<arg value="ps"/>
<!-- Strip the filepaths down to the relevant bit. -->
<arg name="basepath" value="./"/>
<!-- Check up to 8 files simultaneously. -->
<arg name="parallel" value="8"/>
<!-- Check PHP files only. JavaScript and CSS files are checked separately using the @wordpress/scripts package. -->
<arg name="extensions" value="php"/>
<!-- Check all files in this directory and the directories below it. -->
<file>.</file>
<!-- Exclude patterns. -->
<exclude-pattern>/vendor/*</exclude-pattern>
<exclude-pattern>/node_modules/*</exclude-pattern>
<!--
#############################################################################
USE THE WordPress AND THE Theme Review RULESET
#############################################################################
-->
<rule ref="WordPress">
<!-- This rule does not apply here since the _s prefix should be changed by the theme author. -->
<exclude name="WordPress.NamingConventions.PrefixAllGlobals.ShortPrefixPassed"/>
</rule>
<rule ref="WPThemeReview"/>
<!--
#############################################################################
SNIFF SPECIFIC CONFIGURATION
#############################################################################
-->
<!-- Verify that the text_domain is set to the desired text-domain.
Multiple valid text domains can be provided as a comma-delimited list. -->
<rule ref="WordPress.WP.I18n">
<properties>
<property name="text_domain" type="array" value="_s"/>
</properties>
</rule>
<!-- Allow for theme specific exceptions to the file name rules based
on the theme hierarchy. -->
<rule ref="WordPress.Files.FileName">
<properties>
<property name="is_theme" value="true"/>
</properties>
</rule>
<!-- Set the minimum supported WP version. This is used by several sniffs.
The minimum version set here should be in line with the minimum WP version
as set in the "Requires at least" tag in the readme.txt file. -->
<config name="minimum_supported_wp_version" value="4.5"/>
<rule ref="WordPress.Arrays.MultipleStatementAlignment">
<properties>
<!-- No need to adjust alignment of large arrays when the item with the largest key is removed. -->
<property name="exact" value="false"/>
<!-- Don't align multi-line items if ALL items in the array are multi-line. -->
<property name="alignMultilineItems" value="!=100"/>
<!-- Array assignment operator should always be on the same line as the array key. -->
<property name="ignoreNewlines" value="false"/>
</properties>
</rule>
<!-- Verify that everything in the global namespace is prefixed with a theme specific prefix.
Multiple valid prefixes can be provided as a comma-delimited list. -->
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
<properties>
<property name="prefixes" type="array" value="_s" />
</properties>
</rule>
<!--
#############################################################################
USE THE PHPCompatibility RULESET
#############################################################################
-->
<config name="testVersion" value="5.6-"/>
<rule ref="PHPCompatibilityWP"/>
</ruleset>
+39
View File
@@ -0,0 +1,39 @@
=== CyyWordpress ===
Contributors: automattic
Tags: custom-background, custom-logo, custom-menu, featured-images, threaded-comments, translation-ready
Requires at least: 4.5
Tested up to: 5.4
Requires PHP: 5.6
Stable tag: 1.0.0
License: GNU General Public License v2 or later
License URI: LICENSE
A starter theme called CyyWordpress.
== Description ==
Description
== Installation ==
1. In your admin panel, go to Appearance > Themes and click the Add New button.
2. Click Upload Theme and Choose File, then select the theme's .zip file. Click Install Now.
3. Click Activate to use your new theme right away.
== Frequently Asked Questions ==
= Does this theme support any plugins? =
CyyWordpress includes support for WooCommerce and for Infinite Scroll in Jetpack.
== Changelog ==
= 1.0 - May 12 2015 =
* Initial release
== Credits ==
* Based on Underscores https://underscores.me/, (C) 2012-2020 Automattic, Inc., [GPLv2 or later](https://www.gnu.org/licenses/gpl-2.0.html)
* normalize.css https://necolas.github.io/normalize.css/, (C) 2012-2018 Nicolas Gallagher and Jonathan Neal, [MIT](https://opensource.org/licenses/MIT)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

+53
View File
@@ -0,0 +1,53 @@
<?php
/**
* The template for displaying search results pages
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/#search-result
*
* @package CyyWordpress
*/
get_header();
?>
<main id="primary" class="site-main">
<?php if ( have_posts() ) : ?>
<header class="page-header">
<h1 class="page-title">
<?php
/* translators: %s: search query. */
printf( esc_html__( 'Search Results for: %s', 'cyywordpress' ), '<span>' . get_search_query() . '</span>' );
?>
</h1>
</header><!-- .page-header -->
<?php
/* Start the Loop */
while ( have_posts() ) :
the_post();
/**
* Run the loop for the search to output the results.
* If you want to overload this in a child theme then include a file
* called content-search.php and that will be used instead.
*/
get_template_part( 'template-parts/content', 'search' );
endwhile;
the_posts_navigation();
else :
get_template_part( 'template-parts/content', 'none' );
endif;
?>
</main><!-- #main -->
<?php
get_sidebar();
get_footer();
+17
View File
@@ -0,0 +1,17 @@
<?php
/**
* The sidebar containing the main widget area
*
* @link https://developer.wordpress.org/themes/basics/template-files/#template-partials
*
* @package CyyWordpress
*/
if ( ! is_active_sidebar( 'sidebar-1' ) ) {
return;
}
?>
<aside id="secondary" class="widget-area">
<?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside><!-- #secondary -->
+40
View File
@@ -0,0 +1,40 @@
<?php
/**
* The template for displaying all single posts
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/#single-post
*
* @package CyyWordpress
*/
get_header();
?>
<main id="primary" class="site-main">
<?php
while ( have_posts() ) :
the_post();
get_template_part( 'template-parts/content', get_post_type() );
the_post_navigation(
array(
'prev_text' => '<span class="nav-subtitle">' . esc_html__( 'Previous:', 'cyywordpress' ) . '</span> <span class="nav-title">%title</span>',
'next_text' => '<span class="nav-subtitle">' . esc_html__( 'Next:', 'cyywordpress' ) . '</span> <span class="nav-title">%title</span>',
)
);
// If comments are open or we have at least one comment, load up the comment template.
if ( comments_open() || get_comments_number() ) :
comments_template();
endif;
endwhile; // End of the loop.
?>
</main><!-- #main -->
<?php
get_sidebar();
get_footer();
+961
View File
@@ -0,0 +1,961 @@
/*!
Theme Name: CyyWordpress
Theme URI: https://underscores.me/
Author: Automattic
Author URI: https://automattic.com/
Description: Hi. I'm a starter theme called <code>CyyWordpress</code>, or <em>underscores</em>, if you like. I'm a theme meant for hacking so don't use me as a <em>Parent Theme</em>. Instead try turning me into the next, most awesome, WordPress theme out there. That's what I'm here for.
Version: 1.0.0
Tested up to: 5.4
Requires PHP: 5.6
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: CyyWordpress
Tags: custom-background, custom-logo, custom-menu, featured-images, threaded-comments, translation-ready
This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned.
CyyWordpress is based on Underscores https://underscores.me/, (C) 2012-2020 Automattic, Inc.
Underscores is distributed under the terms of the GNU GPL v2 or later.
Normalizing styles have been helped along thanks to the fine work of
Nicolas Gallagher and Jonathan Neal https://necolas.github.io/normalize.css/
*/
/*--------------------------------------------------------------
>>> TABLE OF CONTENTS:
----------------------------------------------------------------
# Generic
- Normalize
- Box sizing
# Base
- Typography
- Elements
- Links
- Forms
## Layouts
# Components
- Navigation
- Posts and pages
- Comments
- Widgets
- Media
- Captions
- Galleries
# plugins
- Jetpack infinite scroll
# Utilities
- Accessibility
- Alignments
--------------------------------------------------------------*/
/*--------------------------------------------------------------
# Generic
--------------------------------------------------------------*/
/* Normalize
--------------------------------------------- */
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15;
-webkit-text-size-adjust: 100%;
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace;
font-size: 1em;
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
padding: 0;
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
/* Box sizing
--------------------------------------------- */
/* Inherit box-sizing to more easily change it's value on a component level.
@link http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
*,
*::before,
*::after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
/*--------------------------------------------------------------
# Base
--------------------------------------------------------------*/
/* Typography
--------------------------------------------- */
body,
button,
input,
select,
optgroup,
textarea {
color: #404040;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
font-size: 1rem;
line-height: 1.5;
}
h1,
h2,
h3,
h4,
h5,
h6 {
clear: both;
}
p {
margin-bottom: 1.5em;
}
dfn,
cite,
em,
i {
font-style: italic;
}
blockquote {
margin: 0 1.5em;
}
address {
margin: 0 0 1.5em;
}
pre {
background: #eee;
font-family: "Courier 10 Pitch", courier, monospace;
line-height: 1.6;
margin-bottom: 1.6em;
max-width: 100%;
overflow: auto;
padding: 1.6em;
}
code,
kbd,
tt,
var {
font-family: monaco, consolas, "Andale Mono", "DejaVu Sans Mono", monospace;
}
abbr,
acronym {
border-bottom: 1px dotted #666;
cursor: help;
}
mark,
ins {
background: #fff9c0;
text-decoration: none;
}
big {
font-size: 125%;
}
/* Elements
--------------------------------------------- */
body {
background: #fff;
}
hr {
background-color: #ccc;
border: 0;
height: 1px;
margin-bottom: 1.5em;
}
ul,
ol {
margin: 0 3em 1.5em 0;
}
ul {
list-style: disc;
}
ol {
list-style: decimal;
}
li > ul,
li > ol {
margin-bottom: 0;
margin-right: 1.5em;
}
dt {
font-weight: 700;
}
dd {
margin: 0 1.5em 1.5em;
}
/* Make sure embeds and iframes fit their containers. */
embed,
iframe,
object {
max-width: 100%;
}
img {
height: auto;
max-width: 100%;
}
figure {
margin: 1em 0;
}
table {
margin: 0 0 1.5em;
width: 100%;
}
/* Links
--------------------------------------------- */
a {
color: #4169e1;
}
a:visited {
color: #800080;
}
a:hover,
a:focus,
a:active {
color: #191970;
}
a:focus {
outline: thin dotted;
}
a:hover,
a:active {
outline: 0;
}
/* Forms
--------------------------------------------- */
button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
border: 1px solid;
border-color: #ccc #ccc #bbb;
border-radius: 3px;
background: #e6e6e6;
color: rgba(0, 0, 0, 0.8);
line-height: 1;
padding: 0.6em 1em 0.4em;
}
button:hover,
input[type="button"]:hover,
input[type="reset"]:hover,
input[type="submit"]:hover {
border-color: #ccc #bbb #aaa;
}
button:active,
button:focus,
input[type="button"]:active,
input[type="button"]:focus,
input[type="reset"]:active,
input[type="reset"]:focus,
input[type="submit"]:active,
input[type="submit"]:focus {
border-color: #aaa #bbb #bbb;
}
input[type="text"],
input[type="email"],
input[type="url"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="tel"],
input[type="range"],
input[type="date"],
input[type="month"],
input[type="week"],
input[type="time"],
input[type="datetime"],
input[type="datetime-local"],
input[type="color"],
textarea {
color: #666;
border: 1px solid #ccc;
border-radius: 3px;
padding: 3px;
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
input[type="tel"]:focus,
input[type="range"]:focus,
input[type="date"]:focus,
input[type="month"]:focus,
input[type="week"]:focus,
input[type="time"]:focus,
input[type="datetime"]:focus,
input[type="datetime-local"]:focus,
input[type="color"]:focus,
textarea:focus {
color: #111;
}
select {
border: 1px solid #ccc;
}
textarea {
width: 100%;
}
/*--------------------------------------------------------------
# Layouts
--------------------------------------------------------------*/
/*--------------------------------------------------------------
# Components
--------------------------------------------------------------*/
/* Navigation
--------------------------------------------- */
.main-navigation {
display: block;
width: 100%;
}
.main-navigation ul {
display: none;
list-style: none;
margin: 0;
padding-right: 0;
}
.main-navigation ul ul {
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
float: right;
position: absolute;
top: 100%;
right: -999em;
z-index: 99999;
}
.main-navigation ul ul ul {
right: -999em;
top: 0;
}
.main-navigation ul ul li:hover > ul,
.main-navigation ul ul li.focus > ul {
display: block;
right: auto;
}
.main-navigation ul ul a {
width: 200px;
}
.main-navigation ul li:hover > ul,
.main-navigation ul li.focus > ul {
right: auto;
}
.main-navigation li {
position: relative;
}
.main-navigation a {
display: block;
text-decoration: none;
}
/* Small menu. */
.menu-toggle,
.main-navigation.toggled ul {
display: block;
}
@media screen and (min-width: 37.5em) {
.menu-toggle {
display: none;
}
.main-navigation ul {
display: flex;
}
}
.site-main .comment-navigation,
.site-main
.posts-navigation,
.site-main
.post-navigation {
margin: 0 0 1.5em;
}
.comment-navigation .nav-links,
.posts-navigation .nav-links,
.post-navigation .nav-links {
display: flex;
}
.comment-navigation .nav-previous,
.posts-navigation .nav-previous,
.post-navigation .nav-previous {
flex: 1 0 50%;
}
.comment-navigation .nav-next,
.posts-navigation .nav-next,
.post-navigation .nav-next {
text-align: end;
flex: 1 0 50%;
}
/* Posts and pages
--------------------------------------------- */
.sticky {
display: block;
}
.post,
.page {
margin: 0 0 1.5em;
}
.updated:not(.published) {
display: none;
}
.page-content,
.entry-content,
.entry-summary {
margin: 1.5em 0 0;
}
.page-links {
clear: both;
margin: 0 0 1.5em;
}
/* Comments
--------------------------------------------- */
.comment-content a {
word-wrap: break-word;
}
.bypostauthor {
display: block;
}
/* Widgets
--------------------------------------------- */
.widget {
margin: 0 0 1.5em;
}
.widget select {
max-width: 100%;
}
/* Media
--------------------------------------------- */
.page-content .wp-smiley,
.entry-content .wp-smiley,
.comment-content .wp-smiley {
border: none;
margin-bottom: 0;
margin-top: 0;
padding: 0;
}
/* Make sure logo link wraps around logo image. */
.custom-logo-link {
display: inline-block;
}
/* Captions
--------------------------------------------- */
.wp-caption {
margin-bottom: 1.5em;
max-width: 100%;
}
.wp-caption img[class*="wp-image-"] {
display: block;
margin-right: auto;
margin-left: auto;
}
.wp-caption .wp-caption-text {
margin: 0.8075em 0;
}
.wp-caption-text {
text-align: center;
}
/* Galleries
--------------------------------------------- */
.gallery {
margin-bottom: 1.5em;
display: grid;
grid-gap: 1.5em;
}
.gallery-item {
display: inline-block;
text-align: center;
width: 100%;
}
.gallery-columns-2 {
grid-template-columns: repeat(2, 1fr);
}
.gallery-columns-3 {
grid-template-columns: repeat(3, 1fr);
}
.gallery-columns-4 {
grid-template-columns: repeat(4, 1fr);
}
.gallery-columns-5 {
grid-template-columns: repeat(5, 1fr);
}
.gallery-columns-6 {
grid-template-columns: repeat(6, 1fr);
}
.gallery-columns-7 {
grid-template-columns: repeat(7, 1fr);
}
.gallery-columns-8 {
grid-template-columns: repeat(8, 1fr);
}
.gallery-columns-9 {
grid-template-columns: repeat(9, 1fr);
}
.gallery-caption {
display: block;
}
/*--------------------------------------------------------------
# Plugins
--------------------------------------------------------------*/
/* Jetpack infinite scroll
--------------------------------------------- */
/* Hide the Posts Navigation and the Footer when Infinite Scroll is in use. */
.infinite-scroll .posts-navigation,
.infinite-scroll.neverending .site-footer {
display: none;
}
/* Re-display the Theme Footer when Infinite Scroll has reached its end. */
.infinity-end.neverending .site-footer {
display: block;
}
/*--------------------------------------------------------------
# Utilities
--------------------------------------------------------------*/
/* Accessibility
--------------------------------------------- */
/* Text meant only for screen readers. */
.screen-reader-text {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute !important;
width: 1px;
word-wrap: normal !important;
}
.screen-reader-text:focus {
background-color: #f1f1f1;
border-radius: 3px;
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
clip: auto !important;
clip-path: none;
color: #21759b;
display: block;
font-size: 0.875rem;
font-weight: 700;
height: auto;
right: 5px;
line-height: normal;
padding: 15px 23px 14px;
text-decoration: none;
top: 5px;
width: auto;
z-index: 100000;
}
/* Do not show the outline on the skip link target. */
#primary[tabindex="-1"]:focus {
outline: 0;
}
/* Alignments
--------------------------------------------- */
.alignleft {
float: left;
margin-right: 1.5em;
margin-bottom: 1.5em;
}
.alignright {
float: right;
margin-left: 1.5em;
margin-bottom: 1.5em;
}
.aligncenter {
clear: both;
display: block;
margin-right: auto;
margin-left: auto;
margin-bottom: 1.5em;
}
+969
View File
@@ -0,0 +1,969 @@
/*!
Theme Name: CyyWordpress
Theme URI: http://underscores.me/
Author: Underscores.me
Author URI: http://underscores.me/
Description: Description
Version: 1.0.0
Tested up to: 5.4
Requires PHP: 5.6
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: cyywordpress
Tags: custom-background, custom-logo, custom-menu, featured-images, threaded-comments, translation-ready
This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned.
CyyWordpress is based on Underscores https://underscores.me/, (C) 2012-2020 Automattic, Inc.
Underscores is distributed under the terms of the GNU GPL v2 or later.
Normalizing styles have been helped along thanks to the fine work of
Nicolas Gallagher and Jonathan Neal https://necolas.github.io/normalize.css/
*/
/*--------------------------------------------------------------
>>> TABLE OF CONTENTS:
----------------------------------------------------------------
# Generic
- Normalize
- Box sizing
# Base
- Typography
- Elements
- Links
- Forms
## Layouts
# Components
- Navigation
- Posts and pages
- Comments
- Widgets
- Media
- Captions
- Galleries
# plugins
- Jetpack infinite scroll
# Utilities
- Accessibility
- Alignments
--------------------------------------------------------------*/
/*--------------------------------------------------------------
# Generic
--------------------------------------------------------------*/
/* Normalize
--------------------------------------------- */
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15;
-webkit-text-size-adjust: 100%;
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace;
font-size: 1em;
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted;
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
padding: 0;
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
/* Box sizing
--------------------------------------------- */
/* Inherit box-sizing to more easily change it's value on a component level.
@link http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */
*,
*::before,
*::after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
/*--------------------------------------------------------------
# Base
--------------------------------------------------------------*/
/* Typography
--------------------------------------------- */
body,
button,
input,
select,
optgroup,
textarea {
color: #404040;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
font-size: 1rem;
line-height: 1.5;
}
h1,
h2,
h3,
h4,
h5,
h6 {
clear: both;
}
p {
margin-bottom: 1.5em;
}
dfn,
cite,
em,
i {
font-style: italic;
}
blockquote {
margin: 0 1.5em;
}
address {
margin: 0 0 1.5em;
}
pre {
background: #eee;
font-family: "Courier 10 Pitch", courier, monospace;
line-height: 1.6;
margin-bottom: 1.6em;
max-width: 100%;
overflow: auto;
padding: 1.6em;
}
code,
kbd,
tt,
var {
font-family: monaco, consolas, "Andale Mono", "DejaVu Sans Mono", monospace;
}
abbr,
acronym {
border-bottom: 1px dotted #666;
cursor: help;
}
mark,
ins {
background: #fff9c0;
text-decoration: none;
}
big {
font-size: 125%;
}
/* Elements
--------------------------------------------- */
body {
background: #fff;
}
hr {
background-color: #ccc;
border: 0;
height: 1px;
margin-bottom: 1.5em;
}
ul,
ol {
margin: 0 0 1.5em 3em;
}
ul {
list-style: disc;
}
ol {
list-style: decimal;
}
li > ul,
li > ol {
margin-bottom: 0;
margin-left: 1.5em;
}
dt {
font-weight: 700;
}
dd {
margin: 0 1.5em 1.5em;
}
/* Make sure embeds and iframes fit their containers. */
embed,
iframe,
object {
max-width: 100%;
}
img {
height: auto;
max-width: 100%;
}
figure {
margin: 1em 0;
}
table {
margin: 0 0 1.5em;
width: 100%;
}
/* Links
--------------------------------------------- */
a {
color: #4169e1;
}
a:visited {
color: #800080;
}
a:hover,
a:focus,
a:active {
color: #191970;
}
a:focus {
outline: thin dotted;
}
a:hover,
a:active {
outline: 0;
}
/* Forms
--------------------------------------------- */
button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
border: 1px solid;
border-color: #ccc #ccc #bbb;
border-radius: 3px;
background: #e6e6e6;
color: rgba(0, 0, 0, 0.8);
line-height: 1;
padding: 0.6em 1em 0.4em;
}
button:hover,
input[type="button"]:hover,
input[type="reset"]:hover,
input[type="submit"]:hover {
border-color: #ccc #bbb #aaa;
}
button:active,
button:focus,
input[type="button"]:active,
input[type="button"]:focus,
input[type="reset"]:active,
input[type="reset"]:focus,
input[type="submit"]:active,
input[type="submit"]:focus {
border-color: #aaa #bbb #bbb;
}
input[type="text"],
input[type="email"],
input[type="url"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="tel"],
input[type="range"],
input[type="date"],
input[type="month"],
input[type="week"],
input[type="time"],
input[type="datetime"],
input[type="datetime-local"],
input[type="color"],
textarea {
color: #666;
border: 1px solid #ccc;
border-radius: 3px;
padding: 3px;
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
input[type="tel"]:focus,
input[type="range"]:focus,
input[type="date"]:focus,
input[type="month"]:focus,
input[type="week"]:focus,
input[type="time"]:focus,
input[type="datetime"]:focus,
input[type="datetime-local"]:focus,
input[type="color"]:focus,
textarea:focus {
color: #111;
}
select {
border: 1px solid #ccc;
}
textarea {
width: 100%;
}
/*--------------------------------------------------------------
# Layouts
--------------------------------------------------------------*/
/*--------------------------------------------------------------
# Components
--------------------------------------------------------------*/
/* Navigation
--------------------------------------------- */
.main-navigation {
display: block;
width: 100%;
}
.main-navigation ul {
display: none;
list-style: none;
margin: 0;
padding-left: 0;
}
.main-navigation ul ul {
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.2);
float: left;
position: absolute;
top: 100%;
left: -999em;
z-index: 99999;
}
.main-navigation ul ul ul {
left: -999em;
top: 0;
}
.main-navigation ul ul li:hover > ul,
.main-navigation ul ul li.focus > ul {
display: block;
left: auto;
}
.main-navigation ul ul a {
width: 200px;
}
.main-navigation ul li:hover > ul,
.main-navigation ul li.focus > ul {
left: auto;
}
.main-navigation li {
position: relative;
}
.main-navigation a {
display: block;
text-decoration: none;
}
/* Small menu. */
.menu-toggle,
.main-navigation.toggled ul {
display: block;
}
@media screen and (min-width: 37.5em) {
.menu-toggle {
display: none;
}
.main-navigation ul {
display: flex;
}
}
.site-main .comment-navigation,
.site-main
.posts-navigation,
.site-main
.post-navigation {
margin: 0 0 1.5em;
}
.comment-navigation .nav-links,
.posts-navigation .nav-links,
.post-navigation .nav-links {
display: flex;
}
.comment-navigation .nav-previous,
.posts-navigation .nav-previous,
.post-navigation .nav-previous {
flex: 1 0 50%;
}
.comment-navigation .nav-next,
.posts-navigation .nav-next,
.post-navigation .nav-next {
text-align: end;
flex: 1 0 50%;
}
/* Posts and pages
--------------------------------------------- */
.sticky {
display: block;
}
.post,
.page {
margin: 0 0 1.5em;
}
.updated:not(.published) {
display: none;
}
.page-content,
.entry-content,
.entry-summary {
margin: 1.5em 0 0;
}
.page-links {
clear: both;
margin: 0 0 1.5em;
}
/* Comments
--------------------------------------------- */
.comment-content a {
word-wrap: break-word;
}
.bypostauthor {
display: block;
}
/* Widgets
--------------------------------------------- */
.widget {
margin: 0 0 1.5em;
}
.widget select {
max-width: 100%;
}
/* Media
--------------------------------------------- */
.page-content .wp-smiley,
.entry-content .wp-smiley,
.comment-content .wp-smiley {
border: none;
margin-bottom: 0;
margin-top: 0;
padding: 0;
}
/* Make sure logo link wraps around logo image. */
.custom-logo-link {
display: inline-block;
}
/* Captions
--------------------------------------------- */
.wp-caption {
margin-bottom: 1.5em;
max-width: 100%;
}
.wp-caption img[class*="wp-image-"] {
display: block;
margin-left: auto;
margin-right: auto;
}
.wp-caption .wp-caption-text {
margin: 0.8075em 0;
}
.wp-caption-text {
text-align: center;
}
/* Galleries
--------------------------------------------- */
.gallery {
margin-bottom: 1.5em;
display: grid;
grid-gap: 1.5em;
}
.gallery-item {
display: inline-block;
text-align: center;
width: 100%;
}
.gallery-columns-2 {
grid-template-columns: repeat(2, 1fr);
}
.gallery-columns-3 {
grid-template-columns: repeat(3, 1fr);
}
.gallery-columns-4 {
grid-template-columns: repeat(4, 1fr);
}
.gallery-columns-5 {
grid-template-columns: repeat(5, 1fr);
}
.gallery-columns-6 {
grid-template-columns: repeat(6, 1fr);
}
.gallery-columns-7 {
grid-template-columns: repeat(7, 1fr);
}
.gallery-columns-8 {
grid-template-columns: repeat(8, 1fr);
}
.gallery-columns-9 {
grid-template-columns: repeat(9, 1fr);
}
.gallery-caption {
display: block;
}
/*--------------------------------------------------------------
# Plugins
--------------------------------------------------------------*/
/* Jetpack infinite scroll
--------------------------------------------- */
/* Hide the Posts Navigation and the Footer when Infinite Scroll is in use. */
.infinite-scroll .posts-navigation,
.infinite-scroll.neverending .site-footer {
display: none;
}
/* Re-display the Theme Footer when Infinite Scroll has reached its end. */
.infinity-end.neverending .site-footer {
display: block;
}
/*--------------------------------------------------------------
# Utilities
--------------------------------------------------------------*/
/* Accessibility
--------------------------------------------- */
/* Text meant only for screen readers. */
.screen-reader-text {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute !important;
width: 1px;
word-wrap: normal !important;
}
.screen-reader-text:focus {
background-color: #f1f1f1;
border-radius: 3px;
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
clip: auto !important;
clip-path: none;
color: #21759b;
display: block;
font-size: 0.875rem;
font-weight: 700;
height: auto;
left: 5px;
line-height: normal;
padding: 15px 23px 14px;
text-decoration: none;
top: 5px;
width: auto;
z-index: 100000;
}
/* Do not show the outline on the skip link target. */
#primary[tabindex="-1"]:focus {
outline: 0;
}
/* Alignments
--------------------------------------------- */
.alignleft {
/*rtl:ignore*/
float: left;
/*rtl:ignore*/
margin-right: 1.5em;
margin-bottom: 1.5em;
}
.alignright {
/*rtl:ignore*/
float: right;
/*rtl:ignore*/
margin-left: 1.5em;
margin-bottom: 1.5em;
}
.aligncenter {
clear: both;
display: block;
margin-left: auto;
margin-right: auto;
margin-bottom: 1.5em;
}
+51
View File
@@ -0,0 +1,51 @@
<?php
/**
* Template part for displaying a message that posts cannot be found
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
?>
<section class="no-results not-found">
<header class="page-header">
<h1 class="page-title"><?php esc_html_e( 'Nothing Found', 'cyywordpress' ); ?></h1>
</header><!-- .page-header -->
<div class="page-content">
<?php
if ( is_home() && current_user_can( 'publish_posts' ) ) :
printf(
'<p>' . wp_kses(
/* translators: 1: link to WP admin new post page. */
__( 'Ready to publish your first post? <a href="%1$s">Get started here</a>.', 'cyywordpress' ),
array(
'a' => array(
'href' => array(),
),
)
) . '</p>',
esc_url( admin_url( 'post-new.php' ) )
);
elseif ( is_search() ) :
?>
<p><?php esc_html_e( 'Sorry, but nothing matched your search terms. Please try again with some different keywords.', 'cyywordpress' ); ?></p>
<?php
get_search_form();
else :
?>
<p><?php esc_html_e( 'It seems we can&rsquo;t find what you&rsquo;re looking for. Perhaps searching can help.', 'cyywordpress' ); ?></p>
<?php
get_search_form();
endif;
?>
</div><!-- .page-content -->
</section><!-- .no-results -->
+54
View File
@@ -0,0 +1,54 @@
<?php
/**
* Template part for displaying page content in page.php
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
</header><!-- .entry-header -->
<?php cyywordpress_post_thumbnail(); ?>
<div class="entry-content">
<?php
the_content();
wp_link_pages(
array(
'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'cyywordpress' ),
'after' => '</div>',
)
);
?>
</div><!-- .entry-content -->
<?php if ( get_edit_post_link() ) : ?>
<footer class="entry-footer">
<?php
edit_post_link(
sprintf(
wp_kses(
/* translators: %s: Name of current post. Only visible to screen readers */
__( 'Edit <span class="screen-reader-text">%s</span>', 'cyywordpress' ),
array(
'span' => array(
'class' => array(),
),
)
),
wp_kses_post( get_the_title() )
),
'<span class="edit-link">',
'</span>'
);
?>
</footer><!-- .entry-footer -->
<?php endif; ?>
</article><!-- #post-<?php the_ID(); ?> -->
+35
View File
@@ -0,0 +1,35 @@
<?php
/**
* Template part for displaying results in search pages
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
<?php if ( 'post' === get_post_type() ) : ?>
<div class="entry-meta">
<?php
cyywordpress_posted_on();
cyywordpress_posted_by();
?>
</div><!-- .entry-meta -->
<?php endif; ?>
</header><!-- .entry-header -->
<?php cyywordpress_post_thumbnail(); ?>
<div class="entry-summary">
<?php the_excerpt(); ?>
</div><!-- .entry-summary -->
<footer class="entry-footer">
<?php cyywordpress_entry_footer(); ?>
</footer><!-- .entry-footer -->
</article><!-- #post-<?php the_ID(); ?> -->
+63
View File
@@ -0,0 +1,63 @@
<?php
/**
* Template part for displaying posts
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package CyyWordpress
*/
?>
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php
if ( is_singular() ) :
the_title( '<h1 class="entry-title">', '</h1>' );
else :
the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
endif;
if ( 'post' === get_post_type() ) :
?>
<div class="entry-meta">
<?php
cyywordpress_posted_on();
cyywordpress_posted_by();
?>
</div><!-- .entry-meta -->
<?php endif; ?>
</header><!-- .entry-header -->
<?php cyywordpress_post_thumbnail(); ?>
<div class="entry-content">
<?php
the_content(
sprintf(
wp_kses(
/* translators: %s: Name of current post. Only visible to screen readers */
__( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'cyywordpress' ),
array(
'span' => array(
'class' => array(),
),
)
),
wp_kses_post( get_the_title() )
)
);
wp_link_pages(
array(
'before' => '<div class="page-links">' . esc_html__( 'Pages:', 'cyywordpress' ),
'after' => '</div>',
)
);
?>
</div><!-- .entry-content -->
<footer class="entry-footer">
<?php cyywordpress_entry_footer(); ?>
</footer><!-- .entry-footer -->
</article><!-- #post-<?php the_ID(); ?> -->
+7
View File
@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3::getLoader();
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../gettext/languages/bin" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/export-plural-rules" "$@"
+4
View File
@@ -0,0 +1,4 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../gettext/languages/bin/export-plural-rules
php "%BIN_TARGET%" %*
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../php-parallel-lint/php-parallel-lint" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/parallel-lint" "$@"
+4
View File
@@ -0,0 +1,4 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../php-parallel-lint/php-parallel-lint/parallel-lint
php "%BIN_TARGET%" %*
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../squizlabs/php_codesniffer/bin" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/phpcbf" "$@"
+4
View File
@@ -0,0 +1,4 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../squizlabs/php_codesniffer/bin/phpcbf
php "%BIN_TARGET%" %*
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../squizlabs/php_codesniffer/bin" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/phpcs" "$@"
+4
View File
@@ -0,0 +1,4 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../squizlabs/php_codesniffer/bin/phpcs
php "%BIN_TARGET%" %*
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../wp-cli/wp-cli/bin" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/wp" "$@"
+4
View File
@@ -0,0 +1,4 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../wp-cli/wp-cli/bin/wp
sh "%BIN_TARGET%" %*
+477
View File
@@ -0,0 +1,477 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
private $vendorDir;
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
private static $registeredLoaders = array();
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
//no-op
} elseif ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}
+460
View File
@@ -0,0 +1,460 @@
<?php
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
class InstalledVersions
{
private static $installed = array (
'root' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
'name' => 'automattic/underscores',
),
'versions' =>
array (
'automattic/underscores' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
),
'dealerdirect/phpcodesniffer-composer-installer' =>
array (
'pretty_version' => 'v0.7.1',
'version' => '0.7.1.0',
'aliases' =>
array (
),
'reference' => 'fe390591e0241955f22eb9ba327d137e501c771c',
),
'gettext/gettext' =>
array (
'pretty_version' => 'v4.8.3',
'version' => '4.8.3.0',
'aliases' =>
array (
),
'reference' => '57ff4fb16647e78e80a5909fe3c190f1c3110321',
),
'gettext/languages' =>
array (
'pretty_version' => '2.6.0',
'version' => '2.6.0.0',
'aliases' =>
array (
),
'reference' => '38ea0482f649e0802e475f0ed19fa993bcb7a618',
),
'grogy/php-parallel-lint' =>
array (
'replaced' =>
array (
0 => '*',
),
),
'jakub-onderka/php-parallel-lint' =>
array (
'replaced' =>
array (
0 => '*',
),
),
'mck89/peast' =>
array (
'pretty_version' => 'v1.12.0',
'version' => '1.12.0.0',
'aliases' =>
array (
),
'reference' => '833be7a294627a8c5b1c482cbf489f73bf9b8086',
),
'mustache/mustache' =>
array (
'pretty_version' => 'v2.13.0',
'version' => '2.13.0.0',
'aliases' =>
array (
),
'reference' => 'e95c5a008c23d3151d59ea72484d4f72049ab7f4',
),
'php-parallel-lint/php-parallel-lint' =>
array (
'pretty_version' => 'v1.2.0',
'version' => '1.2.0.0',
'aliases' =>
array (
),
'reference' => '474f18bc6cc6aca61ca40bfab55139de614e51ca',
),
'phpcompatibility/php-compatibility' =>
array (
'pretty_version' => '9.3.5',
'version' => '9.3.5.0',
'aliases' =>
array (
),
'reference' => '9fb324479acf6f39452e0655d2429cc0d3914243',
),
'phpcompatibility/phpcompatibility-paragonie' =>
array (
'pretty_version' => '1.3.1',
'version' => '1.3.1.0',
'aliases' =>
array (
),
'reference' => 'ddabec839cc003651f2ce695c938686d1086cf43',
),
'phpcompatibility/phpcompatibility-wp' =>
array (
'pretty_version' => '2.1.1',
'version' => '2.1.1.0',
'aliases' =>
array (
),
'reference' => 'b7dc0cd7a8f767ccac5e7637550ea1c50a67b09e',
),
'rmccue/requests' =>
array (
'pretty_version' => 'v1.7.0',
'version' => '1.7.0.0',
'aliases' =>
array (
),
'reference' => '87932f52ffad70504d93f04f15690cf16a089546',
),
'squizlabs/php_codesniffer' =>
array (
'pretty_version' => '3.5.8',
'version' => '3.5.8.0',
'aliases' =>
array (
),
'reference' => '9d583721a7157ee997f235f327de038e7ea6dac4',
),
'symfony/finder' =>
array (
'pretty_version' => 'v5.2.3',
'version' => '5.2.3.0',
'aliases' =>
array (
),
'reference' => '4adc8d172d602008c204c2e16956f99257248e03',
),
'wp-cli/i18n-command' =>
array (
'pretty_version' => 'v2.2.6',
'version' => '2.2.6.0',
'aliases' =>
array (
),
'reference' => 'a66da3f09f6a728832381012848c3074bf1635c8',
),
'wp-cli/mustangostang-spyc' =>
array (
'pretty_version' => '0.6.3',
'version' => '0.6.3.0',
'aliases' =>
array (
),
'reference' => '6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7',
),
'wp-cli/php-cli-tools' =>
array (
'pretty_version' => 'v0.11.11',
'version' => '0.11.11.0',
'aliases' =>
array (
),
'reference' => 'fe9c7c44a9e1bf2196ec51dc38da0593dbf2993f',
),
'wp-cli/wp-cli' =>
array (
'pretty_version' => 'v2.4.1',
'version' => '2.4.1.0',
'aliases' =>
array (
),
'reference' => 'ceb18598e79befa9b2a37a51efbb34910628988b',
),
'wp-coding-standards/wpcs' =>
array (
'pretty_version' => '2.3.0',
'version' => '2.3.0.0',
'aliases' =>
array (
),
'reference' => '7da1894633f168fe244afc6de00d141f27517b62',
),
'wptrt/wpthemereview' =>
array (
'pretty_version' => '0.2.1',
'version' => '0.2.1.0',
'aliases' =>
array (
),
'reference' => '462e59020dad9399ed2fe8e61f2a21b5e206e420',
),
),
);
private static $canGetVendors;
private static $installedByVendor = array();
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
public static function isInstalled($packageName)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return true;
}
}
return false;
}
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
public static function getRawData()
{
return self::$installed;
}
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
}
}
}
$installed[] = self::$installed;
return $installed;
}
}
+21
View File
@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+41
View File
@@ -0,0 +1,41 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'JakubOnderka\\PhpParallelLint\\Application' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Application.php',
'JakubOnderka\\PhpParallelLint\\ArrayIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
'JakubOnderka\\PhpParallelLint\\Blame' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\Error' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
'JakubOnderka\\PhpParallelLint\\Exception' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\FileWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\IWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\JsonOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\Manager' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
'JakubOnderka\\PhpParallelLint\\MultipleWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\NullWriter' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\Output' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\ParallelLint' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
'JakubOnderka\\PhpParallelLint\\Process\\Process' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
'JakubOnderka\\PhpParallelLint\\Result' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Result.php',
'JakubOnderka\\PhpParallelLint\\RunTimeException' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\Settings' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
'JakubOnderka\\PhpParallelLint\\SyntaxError' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Error.php',
'JakubOnderka\\PhpParallelLint\\TextOutput' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\TextOutputColored' => $vendorDir . '/php-parallel-lint/php-parallel-lint/src/Output.php',
);
+12
View File
@@ -0,0 +1,12 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'be01b9b16925dcb22165c40b46681ac6' => $vendorDir . '/wp-cli/php-cli-tools/lib/cli/cli.php',
'3937806105cc8e221b8fa8db5b70d2f2' => $vendorDir . '/wp-cli/mustangostang-spyc/includes/functions.php',
'ffb465a494c3101218c4417180c2c9a2' => $vendorDir . '/wp-cli/i18n-command/i18n-command.php',
);
+13
View File
@@ -0,0 +1,13 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'cli' => array($vendorDir . '/wp-cli/php-cli-tools/lib'),
'WP_CLI' => array($vendorDir . '/wp-cli/wp-cli/php'),
'Requests' => array($vendorDir . '/rmccue/requests/library'),
'Mustache' => array($vendorDir . '/mustache/mustache/src'),
);
+17
View File
@@ -0,0 +1,17 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'WP_CLI\\I18n\\' => array($vendorDir . '/wp-cli/i18n-command/src'),
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
'Peast\\test\\' => array($vendorDir . '/mck89/peast/test/Peast'),
'Peast\\' => array($vendorDir . '/mck89/peast/lib/Peast'),
'Mustangostang\\' => array($vendorDir . '/wp-cli/mustangostang-spyc/src'),
'Gettext\\Languages\\' => array($vendorDir . '/gettext/languages/src'),
'Gettext\\' => array($vendorDir . '/gettext/gettext/src'),
'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'),
);
+75
View File
@@ -0,0 +1,75 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit4def2de59a6f8d56a69341afa83adef3', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire4def2de59a6f8d56a69341afa83adef3($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire4def2de59a6f8d56a69341afa83adef3($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
+155
View File
@@ -0,0 +1,155 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit4def2de59a6f8d56a69341afa83adef3
{
public static $files = array (
'be01b9b16925dcb22165c40b46681ac6' => __DIR__ . '/..' . '/wp-cli/php-cli-tools/lib/cli/cli.php',
'3937806105cc8e221b8fa8db5b70d2f2' => __DIR__ . '/..' . '/wp-cli/mustangostang-spyc/includes/functions.php',
'ffb465a494c3101218c4417180c2c9a2' => __DIR__ . '/..' . '/wp-cli/i18n-command/i18n-command.php',
);
public static $prefixLengthsPsr4 = array (
'W' =>
array (
'WP_CLI\\I18n\\' => 12,
),
'S' =>
array (
'Symfony\\Component\\Finder\\' => 25,
),
'P' =>
array (
'Peast\\test\\' => 11,
'Peast\\' => 6,
),
'M' =>
array (
'Mustangostang\\' => 14,
),
'G' =>
array (
'Gettext\\Languages\\' => 18,
'Gettext\\' => 8,
),
'D' =>
array (
'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => 55,
),
);
public static $prefixDirsPsr4 = array (
'WP_CLI\\I18n\\' =>
array (
0 => __DIR__ . '/..' . '/wp-cli/i18n-command/src',
),
'Symfony\\Component\\Finder\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/finder',
),
'Peast\\test\\' =>
array (
0 => __DIR__ . '/..' . '/mck89/peast/test/Peast',
),
'Peast\\' =>
array (
0 => __DIR__ . '/..' . '/mck89/peast/lib/Peast',
),
'Mustangostang\\' =>
array (
0 => __DIR__ . '/..' . '/wp-cli/mustangostang-spyc/src',
),
'Gettext\\Languages\\' =>
array (
0 => __DIR__ . '/..' . '/gettext/languages/src',
),
'Gettext\\' =>
array (
0 => __DIR__ . '/..' . '/gettext/gettext/src',
),
'Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' =>
array (
0 => __DIR__ . '/..' . '/dealerdirect/phpcodesniffer-composer-installer/src',
),
);
public static $prefixesPsr0 = array (
'c' =>
array (
'cli' =>
array (
0 => __DIR__ . '/..' . '/wp-cli/php-cli-tools/lib',
),
),
'W' =>
array (
'WP_CLI' =>
array (
0 => __DIR__ . '/..' . '/wp-cli/wp-cli/php',
),
),
'R' =>
array (
'Requests' =>
array (
0 => __DIR__ . '/..' . '/rmccue/requests/library',
),
),
'M' =>
array (
'Mustache' =>
array (
0 => __DIR__ . '/..' . '/mustache/mustache/src',
),
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'JakubOnderka\\PhpParallelLint\\Application' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Application.php',
'JakubOnderka\\PhpParallelLint\\ArrayIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
'JakubOnderka\\PhpParallelLint\\Blame' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
'JakubOnderka\\PhpParallelLint\\CheckstyleOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\ConsoleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\Error' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
'JakubOnderka\\PhpParallelLint\\ErrorFormatter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ErrorFormatter.php',
'JakubOnderka\\PhpParallelLint\\Exception' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\FileWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\IWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\InvalidArgumentException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\JsonOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\Manager' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
'JakubOnderka\\PhpParallelLint\\MultipleWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\NotExistsPathException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\NullWriter' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\Output' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\ParallelLint' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/ParallelLint.php',
'JakubOnderka\\PhpParallelLint\\Process\\GitBlameProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/GitBlameProcess.php',
'JakubOnderka\\PhpParallelLint\\Process\\LintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/LintProcess.php',
'JakubOnderka\\PhpParallelLint\\Process\\PhpExecutable' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpExecutable.php',
'JakubOnderka\\PhpParallelLint\\Process\\PhpProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/PhpProcess.php',
'JakubOnderka\\PhpParallelLint\\Process\\Process' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/Process.php',
'JakubOnderka\\PhpParallelLint\\Process\\SkipLintProcess' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Process/SkipLintProcess.php',
'JakubOnderka\\PhpParallelLint\\RecursiveDirectoryFilterIterator' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Manager.php',
'JakubOnderka\\PhpParallelLint\\Result' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Result.php',
'JakubOnderka\\PhpParallelLint\\RunTimeException' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/exceptions.php',
'JakubOnderka\\PhpParallelLint\\Settings' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Settings.php',
'JakubOnderka\\PhpParallelLint\\SyntaxError' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Error.php',
'JakubOnderka\\PhpParallelLint\\TextOutput' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
'JakubOnderka\\PhpParallelLint\\TextOutputColored' => __DIR__ . '/..' . '/php-parallel-lint/php-parallel-lint/src/Output.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$prefixesPsr0;
$loader->classMap = ComposerStaticInit4def2de59a6f8d56a69341afa83adef3::$classMap;
}, null, ClassLoader::class);
}
}
+1137
View File
File diff suppressed because it is too large Load Diff
+200
View File
@@ -0,0 +1,200 @@
<?php return array (
'root' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
'name' => 'automattic/underscores',
),
'versions' =>
array (
'automattic/underscores' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
),
'dealerdirect/phpcodesniffer-composer-installer' =>
array (
'pretty_version' => 'v0.7.1',
'version' => '0.7.1.0',
'aliases' =>
array (
),
'reference' => 'fe390591e0241955f22eb9ba327d137e501c771c',
),
'gettext/gettext' =>
array (
'pretty_version' => 'v4.8.3',
'version' => '4.8.3.0',
'aliases' =>
array (
),
'reference' => '57ff4fb16647e78e80a5909fe3c190f1c3110321',
),
'gettext/languages' =>
array (
'pretty_version' => '2.6.0',
'version' => '2.6.0.0',
'aliases' =>
array (
),
'reference' => '38ea0482f649e0802e475f0ed19fa993bcb7a618',
),
'grogy/php-parallel-lint' =>
array (
'replaced' =>
array (
0 => '*',
),
),
'jakub-onderka/php-parallel-lint' =>
array (
'replaced' =>
array (
0 => '*',
),
),
'mck89/peast' =>
array (
'pretty_version' => 'v1.12.0',
'version' => '1.12.0.0',
'aliases' =>
array (
),
'reference' => '833be7a294627a8c5b1c482cbf489f73bf9b8086',
),
'mustache/mustache' =>
array (
'pretty_version' => 'v2.13.0',
'version' => '2.13.0.0',
'aliases' =>
array (
),
'reference' => 'e95c5a008c23d3151d59ea72484d4f72049ab7f4',
),
'php-parallel-lint/php-parallel-lint' =>
array (
'pretty_version' => 'v1.2.0',
'version' => '1.2.0.0',
'aliases' =>
array (
),
'reference' => '474f18bc6cc6aca61ca40bfab55139de614e51ca',
),
'phpcompatibility/php-compatibility' =>
array (
'pretty_version' => '9.3.5',
'version' => '9.3.5.0',
'aliases' =>
array (
),
'reference' => '9fb324479acf6f39452e0655d2429cc0d3914243',
),
'phpcompatibility/phpcompatibility-paragonie' =>
array (
'pretty_version' => '1.3.1',
'version' => '1.3.1.0',
'aliases' =>
array (
),
'reference' => 'ddabec839cc003651f2ce695c938686d1086cf43',
),
'phpcompatibility/phpcompatibility-wp' =>
array (
'pretty_version' => '2.1.1',
'version' => '2.1.1.0',
'aliases' =>
array (
),
'reference' => 'b7dc0cd7a8f767ccac5e7637550ea1c50a67b09e',
),
'rmccue/requests' =>
array (
'pretty_version' => 'v1.7.0',
'version' => '1.7.0.0',
'aliases' =>
array (
),
'reference' => '87932f52ffad70504d93f04f15690cf16a089546',
),
'squizlabs/php_codesniffer' =>
array (
'pretty_version' => '3.5.8',
'version' => '3.5.8.0',
'aliases' =>
array (
),
'reference' => '9d583721a7157ee997f235f327de038e7ea6dac4',
),
'symfony/finder' =>
array (
'pretty_version' => 'v5.2.3',
'version' => '5.2.3.0',
'aliases' =>
array (
),
'reference' => '4adc8d172d602008c204c2e16956f99257248e03',
),
'wp-cli/i18n-command' =>
array (
'pretty_version' => 'v2.2.6',
'version' => '2.2.6.0',
'aliases' =>
array (
),
'reference' => 'a66da3f09f6a728832381012848c3074bf1635c8',
),
'wp-cli/mustangostang-spyc' =>
array (
'pretty_version' => '0.6.3',
'version' => '0.6.3.0',
'aliases' =>
array (
),
'reference' => '6aa0b4da69ce9e9a2c8402dab8d43cf32c581cc7',
),
'wp-cli/php-cli-tools' =>
array (
'pretty_version' => 'v0.11.11',
'version' => '0.11.11.0',
'aliases' =>
array (
),
'reference' => 'fe9c7c44a9e1bf2196ec51dc38da0593dbf2993f',
),
'wp-cli/wp-cli' =>
array (
'pretty_version' => 'v2.4.1',
'version' => '2.4.1.0',
'aliases' =>
array (
),
'reference' => 'ceb18598e79befa9b2a37a51efbb34910628988b',
),
'wp-coding-standards/wpcs' =>
array (
'pretty_version' => '2.3.0',
'version' => '2.3.0.0',
'aliases' =>
array (
),
'reference' => '7da1894633f168fe244afc6de00d141f27517b62',
),
'wptrt/wpthemereview' =>
array (
'pretty_version' => '0.2.1',
'version' => '0.2.1.0',
'aliases' =>
array (
),
'reference' => '462e59020dad9399ed2fe8e61f2a21b5e206e420',
),
),
);
+26
View File
@@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50600)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}
@@ -0,0 +1,6 @@
{
"plugins": [
"remark-preset-lint-recommended",
["remark-lint-list-item-indent", "space"]
]
}
@@ -0,0 +1,6 @@
---
extends: default
rules:
line-length:
level: warning
max: 120
@@ -0,0 +1,129 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<potherca@gmail.com>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016-2020 Dealerdirect B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,251 @@
# PHP_CodeSniffer Standards Composer Installer Plugin
![Project Stage][project-stage-shield]
![Last Commit][last-updated-shield]
![Awesome][awesome-shield]
[![License][license-shield]](LICENSE.md)
[![Travis][travis-shield]][travis]
[![Scrutinizer][scrutinizer-shield]][scrutinizer]
[![Latest Version on Packagist][packagist-version-shield]][packagist-version]
[![Packagist][packagist-shield]][packagist]
[![Contributor Covenant][code-of-conduct-shield]][code-of-conduct]
This composer installer plugin allows for easy installation of [PHP_CodeSniffer][codesniffer] coding standards (rulesets).
No more symbolic linking of directories, checking out repositories on specific locations or changing
the `phpcs` configuration.
_Note: This plugin is compatible with both version 2.x and 3.x of_ [PHP_CodeSniffer][codesniffer]
## Usage
Installation can be done with [Composer][composer], by requiring this package as a development dependency:
```bash
composer require --dev dealerdirect/phpcodesniffer-composer-installer
```
That's it.
### How it works
Basically, this plugin executes the following steps:
- This plugin searches for `phpcodesniffer-standard` packages in all of your currently installed Composer packages.
- Matching packages and the project itself are scanned for PHP_CodeSniffer rulesets.
- The plugin will call PHP_CodeSniffer and configure the `installed_paths` option.
### Example project
The following is an example Composer project and has included
multiple `phpcodesniffer-standard` packages.
```json
{
"name": "dealerdirect/example-project",
"description": "Just an example project",
"type": "project",
"require": {},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "*",
"object-calisthenics/phpcs-calisthenics-rules": "*",
"phpcompatibility/php-compatibility": "*",
"wp-coding-standards/wpcs": "*"
}
}
```
After running `composer install` PHP_CodeSniffer just works:
```bash
$ ./vendor/bin/phpcs -i
The installed coding standards are MySource, PEAR, PSR1, PSR2, Squiz, Zend, PHPCompatibility, WordPress,
WordPress-Core, WordPress-Docs, WordPress-Extra and WordPress-VIP
```
### Calling the plugin directly
In some circumstances, it is desirable to call this plugin's functionality
directly. For instance, during development or in [CI][definition-ci] environments.
As the plugin requires Composer to work, direct calls need to be wired through a
project's `composer.json`.
This is done by adding a call to the `Plugin::run` function in the `script`
section of the `composer.json`:
```json
{
"scripts": {
"install-codestandards": [
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"
]
}
}
```
The command can then be called using `composer run-script install-codestandards` or
referenced from other script configurations, as follows:
```json
{
"scripts": {
"install-codestandards": [
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"
],
"post-install-cmd": [
"@install-codestandards"
]
}
}
```
For more details about Composer scripts, please refer to [the section on scripts
in the Composer manual][composer-manual-scripts].
### Changing the Coding Standards search depth
By default, this plugin searches up for Coding Standards up to three directories
deep. In most cases, this should be sufficient. However, this plugin allows
you to customize the search depth setting if needed.
```json
{
"extra": {
"phpcodesniffer-search-depth": 5
}
}
```
### Caveats
When this plugin is installed globally, composer will load the _global_ plugin rather
than the one from the local repository. Despite [this behavior being documented
in the composer manual][using-composer-plugins], it could potentially confuse
as another version of the plugin could be run and not the one specified by the project.
## Developing Coding Standards
Coding standard can be developed normally, as documented by [PHP_CodeSniffer][codesniffer], in the [Coding Standard Tutorial][tutorial].
Create a composer package of your coding standard by adding a `composer.json` file.
```json
{
"name" : "acme/phpcodesniffer-our-standards",
"description" : "Package contains all coding standards of the Acme company",
"require" : {
"php" : ">=5.4.0,<8.0.0-dev",
"squizlabs/php_codesniffer" : "^3.0"
},
"type" : "phpcodesniffer-standard"
}
```
Requirements:
* The repository may contain one or more standards.
* Each standard can have a separate directory no deeper than 3 levels from the repository root.
* The package `type` must be `phpcodesniffer-standard`. Without this, the plugin will not trigger.
### Requiring the plugin from within your coding standard
If your coding standard itself depends on additional external PHPCS standards, this plugin can
make life easier on your end-users by taking care of the installation of all standards - yours
and your dependencies - for them.
This can help reduce the number of support questions about setting the `installed_paths`, as well
as simplify your standard's installation instructions.
For this to work, make sure your external standard adds this plugin to the `composer.json` config
via `require`, **not** `require-dev`.
> :warning: Your end-user may already `require-dev` this plugin and/or other external standards used
> by your end-users may require this plugin as well.
>
> To prevent your end-users getting into "_dependency hell_", make sure to make the version requirement
> for this plugin flexible.
>
> As, for now, this plugin is still regarded as "unstable" (version < 1.0), remember that Composer
> treats unstable minors as majors and will not be able to resolve one config requiring this plugin
> at version `^0.5`, while another requires it at version `^0.6`.
> Either allow multiple minors or use `*` as the version requirement.
>
> Some examples of flexible requirements which can be used:
> ```bash
> composer require dealerdirect/phpcodesniffer-composer-installer:"*"
> composer require dealerdirect/phpcodesniffer-composer-installer:"0.*"
> composer require dealerdirect/phpcodesniffer-composer-installer:"^0.4 || ^0.5 || ^0.6"
> ```
## Changelog
This repository does not contain a `CHANGELOG.md` file, however, we do publish a changelog on each release
using the [GitHub releases][changelog] functionality.
## Contributing
This is an active open-source project. We are always open to people who want to
use the code or contribute to it.
We've set up a separate document for our [contribution guidelines][contributing-guidelines].
Thank you for being involved! :heart_eyes:
## Authors & contributors
The original idea and setup of this repository is by [Franck Nijhof][frenck], employee @ Dealerdirect.
For a full list of all author and/or contributors, check [the contributors page][contributors].
## License
The MIT License (MIT)
Copyright (c) 2016-2020 Dealerdirect B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
[awesome-shield]: https://img.shields.io/badge/awesome%3F-yes-brightgreen.svg
[changelog]: https://github.com/Dealerdirect/phpcodesniffer-composer-installer/releases
[code-of-conduct-shield]: https://img.shields.io/badge/Contributor%20Covenant-v2.0-ff69b4.svg
[code-of-conduct]: CODE_OF_CONDUCT.md
[codesniffer]: https://github.com/squizlabs/PHP_CodeSniffer
[composer-manual-scripts]: https://getcomposer.org/doc/articles/scripts.md
[composer]: https://getcomposer.org/
[contributing-guidelines]: CONTRIBUTING.md
[contributors]: https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors
[definition-ci]: https://en.wikipedia.org/wiki/Continuous_integration
[frenck]: https://github.com/frenck
[last-updated-shield]: https://img.shields.io/github/last-commit/Dealerdirect/phpcodesniffer-composer-installer.svg
[license-shield]: https://img.shields.io/github/license/dealerdirect/phpcodesniffer-composer-installer.svg
[packagist-shield]: https://img.shields.io/packagist/dt/dealerdirect/phpcodesniffer-composer-installer.svg
[packagist-version-shield]: https://img.shields.io/packagist/v/dealerdirect/phpcodesniffer-composer-installer.svg
[packagist-version]: https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer
[packagist]: https://packagist.org/packages/dealerdirect/phpcodesniffer-composer-installer
[project-stage-shield]: https://img.shields.io/badge/Project%20Stage-Development-yellowgreen.svg
[scrutinizer-shield]: https://img.shields.io/scrutinizer/g/dealerdirect/phpcodesniffer-composer-installer.svg
[scrutinizer]: https://scrutinizer-ci.com/g/dealerdirect/phpcodesniffer-composer-installer/
[travis-shield]: https://img.shields.io/travis/Dealerdirect/phpcodesniffer-composer-installer.svg
[travis]: https://travis-ci.org/Dealerdirect/phpcodesniffer-composer-installer
[tutorial]: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Coding-Standard-Tutorial
[using-composer-plugins]: https://getcomposer.org/doc/articles/plugins.md#using-plugins
@@ -0,0 +1,50 @@
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"type": "composer-plugin",
"keywords": [
"composer", "installer", "plugin",
"phpcs", "codesniffer", "phpcodesniffer", "php_codesniffer",
"standard", "standards", "style guide", "stylecheck",
"qa", "quality", "code quality", "tests"
],
"homepage": "http://www.dealerdirect.com",
"license": "MIT",
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
}
],
"support": {
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
},
"require": {
"php": ">=5.3",
"composer-plugin-api": "^1.0 || ^2.0",
"squizlabs/php_codesniffer": "^2.0 || ^3.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"sensiolabs/security-checker": "^4.1.0",
"phpcompatibility/php-compatibility": "^9.0"
},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"extra": {
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"scripts": {
"install-codestandards": [
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"
]
}
}
@@ -0,0 +1,620 @@
<?php
/**
* This file is part of the Dealerdirect PHP_CodeSniffer Standards
* Composer Installer Plugin package.
*
* @copyright 2016-2020 Dealerdirect B.V.
* @license MIT
*/
namespace Dealerdirect\Composer\Plugin\Installers\PHPCodeSniffer;
use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Package\AliasPackage;
use Composer\Package\PackageInterface;
use Composer\Package\RootPackageInterface;
use Composer\Plugin\PluginInterface;
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Composer\Util\Filesystem;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Process\Exception\LogicException;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Exception\RuntimeException;
use Symfony\Component\Process\PhpExecutableFinder;
/**
* PHP_CodeSniffer standard installation manager.
*
* @author Franck Nijhof <franck.nijhof@dealerdirect.com>
*/
class Plugin implements PluginInterface, EventSubscriberInterface
{
const KEY_MAX_DEPTH = 'phpcodesniffer-search-depth';
const MESSAGE_ERROR_WRONG_MAX_DEPTH =
'The value of "%s" (in the composer.json "extra".section) must be an integer larger then %d, %s given.';
const MESSAGE_NOT_INSTALLED = 'PHPCodeSniffer is not installed';
const MESSAGE_NOTHING_TO_INSTALL = 'Nothing to install or update';
const MESSAGE_PLUGIN_UNINSTALLED = 'PHPCodeSniffer Composer Installer is uninstalled';
const MESSAGE_RUNNING_INSTALLER = 'Running PHPCodeSniffer Composer Installer';
const PACKAGE_NAME = 'squizlabs/php_codesniffer';
const PACKAGE_TYPE = 'phpcodesniffer-standard';
const PHPCS_CONFIG_REGEX = '`%s:[^\r\n]+`';
const PHPCS_CONFIG_KEY = 'installed_paths';
const PLUGIN_NAME = 'dealerdirect/phpcodesniffer-composer-installer';
/**
* @var Composer
*/
private $composer;
/**
* @var string
*/
private $cwd;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var array
*/
private $installedPaths;
/**
* @var IOInterface
*/
private $io;
/**
* @var ProcessExecutor
*/
private $processExecutor;
/**
* Triggers the plugin's main functionality.
*
* Makes it possible to run the plugin as a custom command.
*
* @param Event $event
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
public static function run(Event $event)
{
$io = $event->getIO();
$composer = $event->getComposer();
$instance = new static();
$instance->io = $io;
$instance->composer = $composer;
$instance->init();
$instance->onDependenciesChangedEvent();
}
/**
* {@inheritDoc}
*
* @throws \RuntimeException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
public function activate(Composer $composer, IOInterface $io)
{
$this->composer = $composer;
$this->io = $io;
$this->init();
}
/**
* {@inheritDoc}
*/
public function deactivate(Composer $composer, IOInterface $io)
{
}
/**
* {@inheritDoc}
*/
public function uninstall(Composer $composer, IOInterface $io)
{
}
/**
* Prepares the plugin so it's main functionality can be run.
*
* @throws \RuntimeException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
private function init()
{
$this->cwd = getcwd();
$this->installedPaths = array();
$this->processExecutor = new ProcessExecutor($this->io);
$this->filesystem = new Filesystem($this->processExecutor);
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
ScriptEvents::POST_INSTALL_CMD => array(
array('onDependenciesChangedEvent', 0),
),
ScriptEvents::POST_UPDATE_CMD => array(
array('onDependenciesChangedEvent', 0),
),
);
}
/**
* Entry point for post install and post update events.
*
* @throws \InvalidArgumentException
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
public function onDependenciesChangedEvent()
{
$io = $this->io;
$isVerbose = $io->isVerbose();
$exitCode = 0;
if ($isVerbose) {
$io->write(sprintf('<info>%s</info>', self::MESSAGE_RUNNING_INSTALLER));
}
if ($this->isPHPCodeSnifferInstalled() === true) {
$this->loadInstalledPaths();
$installPathCleaned = $this->cleanInstalledPaths();
$installPathUpdated = $this->updateInstalledPaths();
if ($installPathCleaned === true || $installPathUpdated === true) {
$exitCode = $this->saveInstalledPaths();
} elseif ($isVerbose) {
$io->write(sprintf('<info>%s</info>', self::MESSAGE_NOTHING_TO_INSTALL));
}
} else {
$pluginPackage = $this
->composer
->getRepositoryManager()
->getLocalRepository()
->findPackages(self::PLUGIN_NAME)
;
$isPluginUninstalled = count($pluginPackage) === 0;
if ($isPluginUninstalled) {
if ($isVerbose) {
$io->write(sprintf('<info>%s</info>', self::MESSAGE_PLUGIN_UNINSTALLED));
}
} else {
$exitCode = 1;
if ($isVerbose) {
$io->write(sprintf('<error>%s</error>', self::MESSAGE_NOT_INSTALLED));
}
}
}
return $exitCode;
}
/**
* Load all paths from PHP_CodeSniffer into an array.
*
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*/
private function loadInstalledPaths()
{
if ($this->isPHPCodeSnifferInstalled() === true) {
$this->processExecutor->execute(
sprintf(
'phpcs --config-show %s',
self::PHPCS_CONFIG_KEY
),
$output,
$this->composer->getConfig()->get('bin-dir')
);
$regex = sprintf(self::PHPCS_CONFIG_REGEX, self::PHPCS_CONFIG_KEY);
if (preg_match($regex, $output, $match) === 1) {
$phpcsInstalledPaths = str_replace(self::PHPCS_CONFIG_KEY . ': ', '', $match[0]);
$phpcsInstalledPaths = trim($phpcsInstalledPaths);
if ($phpcsInstalledPaths !== '') {
$this->installedPaths = explode(',', $phpcsInstalledPaths);
}
}
}
}
/**
* Save all coding standard paths back into PHP_CodeSniffer
*
* @throws LogicException
* @throws ProcessFailedException
* @throws RuntimeException
*
* @return int Exit code. 0 for success, 1 or higher for failure.
*/
private function saveInstalledPaths()
{
// Check if we found installed paths to set.
if (count($this->installedPaths) !== 0) {
sort($this->installedPaths);
$paths = implode(',', $this->installedPaths);
$arguments = array('--config-set', self::PHPCS_CONFIG_KEY, $paths);
$configMessage = sprintf(
'PHP CodeSniffer Config <info>%s</info> <comment>set to</comment> <info>%s</info>',
self::PHPCS_CONFIG_KEY,
$paths
);
} else {
// Delete the installed paths if none were found.
$arguments = array('--config-delete', self::PHPCS_CONFIG_KEY);
$configMessage = sprintf(
'PHP CodeSniffer Config <info>%s</info> <comment>delete</comment>',
self::PHPCS_CONFIG_KEY
);
}
// Prepare message in case of failure
$failMessage = sprintf(
'Failed to set PHP CodeSniffer <info>%s</info> Config',
self::PHPCS_CONFIG_KEY
);
// Determine the path to the main PHPCS file.
$phpcsPath = $this->getPHPCodeSnifferInstallPath();
if (file_exists($phpcsPath . '/bin/phpcs') === true) {
// PHPCS 3.x.
$phpcsExecutable = './bin/phpcs';
} else {
// PHPCS 2.x.
$phpcsExecutable = './scripts/phpcs';
}
// Okay, lets rock!
$command = vsprintf(
'%s %s %s',
array(
'php executable' => $this->getPhpExecCommand(),
'phpcs executable' => $phpcsExecutable,
'arguments' => implode(' ', $arguments)
)
);
$exitCode = $this->processExecutor->execute($command, $configResult, $phpcsPath);
if ($exitCode === 0) {
$exitCode = $this->verifySaveSuccess();
}
if ($exitCode === 0) {
$this->io->write($configMessage);
} else {
$this->io->write($failMessage);
}
if ($this->io->isVerbose() && !empty($configResult)) {
$this->io->write(sprintf('<info>%s</info>', $configResult));
}
return $exitCode;
}
/**
* Verify that the paths which were expected to be saved, have been.
*
* @return int Exit code. 0 for success, 1 for failure.
*/
private function verifySaveSuccess()
{
$exitCode = 1;
$expectedPaths = $this->installedPaths;
// Request the currently set installed paths after the save.
$this->loadInstalledPaths();
$registeredPaths = array_intersect($this->installedPaths, $expectedPaths);
$registeredCount = count($registeredPaths);
$expectedCount = count($expectedPaths);
if ($expectedCount === $registeredCount) {
$exitCode = 0;
}
if ($exitCode === 1 && $this->io->isVerbose()) {
$verificationMessage = sprintf(
"Paths to external standards found by the plugin: <info>%s</info>\n"
. 'Actual paths registered with PHPCS: <info>%s</info>',
implode(', ', $expectedPaths),
implode(', ', $this->installedPaths)
);
$this->io->write($verificationMessage);
}
return $exitCode;
}
/**
* Get the path to the current PHP version being used.
*
* Duplicate of the same in the EventDispatcher class in Composer itself.
*/
protected function getPhpExecCommand()
{
$finder = new PhpExecutableFinder();
$phpPath = $finder->find(false);
if ($phpPath === false) {
throw new \RuntimeException('Failed to locate PHP binary to execute ' . $phpPath);
}
$phpArgs = $finder->findArguments();
$phpArgs = $phpArgs
? ' ' . implode(' ', $phpArgs)
: ''
;
$command = ProcessExecutor::escape($phpPath) .
$phpArgs .
' -d allow_url_fopen=' . ProcessExecutor::escape(ini_get('allow_url_fopen')) .
' -d disable_functions=' . ProcessExecutor::escape(ini_get('disable_functions')) .
' -d memory_limit=' . ProcessExecutor::escape(ini_get('memory_limit'))
;
return $command;
}
/**
* Iterate trough all known paths and check if they are still valid.
*
* If path does not exists, is not an directory or isn't readable, the path
* is removed from the list.
*
* @return bool True if changes where made, false otherwise
*/
private function cleanInstalledPaths()
{
$changes = false;
foreach ($this->installedPaths as $key => $path) {
// This might be a relative path as well
$alternativePath = realpath($this->getPHPCodeSnifferInstallPath() . DIRECTORY_SEPARATOR . $path);
if (
(is_dir($path) === false || is_readable($path) === false) &&
(is_dir($alternativePath) === false || is_readable($alternativePath) === false)
) {
unset($this->installedPaths[$key]);
$changes = true;
}
}
return $changes;
}
/**
* Check all installed packages (including the root package) against
* the installed paths from PHP_CodeSniffer and add the missing ones.
*
* @return bool True if changes where made, false otherwise
*
* @throws \InvalidArgumentException
* @throws \RuntimeException
*/
private function updateInstalledPaths()
{
$changes = false;
$searchPaths = array($this->cwd);
$codingStandardPackages = $this->getPHPCodingStandardPackages();
foreach ($codingStandardPackages as $package) {
$installPath = $this->composer->getInstallationManager()->getInstallPath($package);
if ($this->filesystem->isAbsolutePath($installPath) === false) {
$installPath = $this->filesystem->normalizePath(
$this->cwd . DIRECTORY_SEPARATOR . $installPath
);
}
$searchPaths[] = $installPath;
}
$finder = new Finder();
$finder->files()
->depth('<= ' . $this->getMaxDepth())
->depth('>= ' . $this->getMinDepth())
->ignoreUnreadableDirs()
->ignoreVCS(true)
->in($searchPaths)
->name('ruleset.xml');
// Process each found possible ruleset.
foreach ($finder as $ruleset) {
$standardsPath = $ruleset->getPath();
// Pick the directory above the directory containing the standard, unless this is the project root.
if ($standardsPath !== $this->cwd) {
$standardsPath = dirname($standardsPath);
}
// Use relative paths for local project repositories.
if ($this->isRunningGlobally() === false) {
$standardsPath = $this->filesystem->findShortestPath(
$this->getPHPCodeSnifferInstallPath(),
$standardsPath,
true
);
}
// De-duplicate and add when directory is not configured.
if (in_array($standardsPath, $this->installedPaths, true) === false) {
$this->installedPaths[] = $standardsPath;
$changes = true;
}
}
return $changes;
}
/**
* Iterates through Composers' local repository looking for valid Coding
* Standard packages.
*
* If the package is the RootPackage (the one the plugin is installed into),
* the package is ignored for now since it needs a different install path logic.
*
* @return array Composer packages containing coding standard(s)
*/
private function getPHPCodingStandardPackages()
{
$codingStandardPackages = array_filter(
$this->composer->getRepositoryManager()->getLocalRepository()->getPackages(),
function (PackageInterface $package) {
if ($package instanceof AliasPackage) {
return false;
}
return $package->getType() === Plugin::PACKAGE_TYPE;
}
);
if (
! $this->composer->getPackage() instanceof RootPackageInterface
&& $this->composer->getPackage()->getType() === self::PACKAGE_TYPE
) {
$codingStandardPackages[] = $this->composer->getPackage();
}
return $codingStandardPackages;
}
/**
* Searches for the installed PHP_CodeSniffer Composer package
*
* @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against
*
* @return PackageInterface|null
*/
private function getPHPCodeSnifferPackage($versionConstraint = null)
{
$packages = $this
->composer
->getRepositoryManager()
->getLocalRepository()
->findPackages(self::PACKAGE_NAME, $versionConstraint);
return array_shift($packages);
}
/**
* Returns the path to the PHP_CodeSniffer package installation location
*
* @return string
*/
private function getPHPCodeSnifferInstallPath()
{
return $this->composer->getInstallationManager()->getInstallPath($this->getPHPCodeSnifferPackage());
}
/**
* Simple check if PHP_CodeSniffer is installed.
*
* @param null|string|\Composer\Semver\Constraint\ConstraintInterface $versionConstraint to match against
*
* @return bool Whether PHP_CodeSniffer is installed
*/
private function isPHPCodeSnifferInstalled($versionConstraint = null)
{
return ($this->getPHPCodeSnifferPackage($versionConstraint) !== null);
}
/**
* Test if composer is running "global"
* This check kinda dirty, but it is the "Composer Way"
*
* @return bool Whether Composer is running "globally"
*
* @throws \RuntimeException
*/
private function isRunningGlobally()
{
return ($this->composer->getConfig()->get('home') === $this->cwd);
}
/**
* Determines the maximum search depth when searching for Coding Standards.
*
* @return int
*
* @throws \InvalidArgumentException
*/
private function getMaxDepth()
{
$maxDepth = 3;
$extra = $this->composer->getPackage()->getExtra();
if (array_key_exists(self::KEY_MAX_DEPTH, $extra)) {
$maxDepth = $extra[self::KEY_MAX_DEPTH];
$minDepth = $this->getMinDepth();
if (
(string) (int) $maxDepth !== (string) $maxDepth /* Must be an integer or cleanly castable to one */
|| $maxDepth <= $minDepth /* Larger than the minimum */
|| is_float($maxDepth) === true /* Within the boundaries of integer */
) {
$message = vsprintf(
self::MESSAGE_ERROR_WRONG_MAX_DEPTH,
array(
'key' => self::KEY_MAX_DEPTH,
'min' => $minDepth,
'given' => var_export($maxDepth, true),
)
);
throw new \InvalidArgumentException($message);
}
}
return (int) $maxDepth;
}
/**
* Returns the minimal search depth for Coding Standard packages.
*
* Usually this is 0, unless PHP_CodeSniffer >= 3 is used.
*
* @return int
*/
private function getMinDepth()
{
if ($this->isPHPCodeSnifferInstalled('>= 3.0.0') !== true) {
return 1;
}
return 0;
}
}
+178
View File
@@ -0,0 +1,178 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
Previous releases are documented in [github releases](https://github.com/oscarotero/Gettext/releases)
## [4.8.3] - 2020-11-18
### Fixed
- Blade extractor for Laravel8/Jetstream [#261]
## [4.8.2] - 2019-12-02
### Fixed
- UTF-8 handling for VueJs extractor [#242]
## [4.8.1] - 2019-11-15
### Fixed
- Php error when scanning for a single domain but other string found [#238]
## [4.8.0] - 2019-11-04
### Changed
- Many `private` properties and methods were changed to `protected` in order to improve the extensibility [#231]
### Fixed
- PHP 7.4 support [#230]
## [4.7.0] - 2019-10-07
### Added
- Support for UnitID in Xliff [#221] [#224] [#225]
- Support for scan multiple domains at the same time [#223]
### Fixed
- New lines in windows [#218] [#226]
## [4.6.3] - 2019-07-15
### Added
- Some VueJs extraction improvements and additions [#205], [#213]
### Fixed
- Multiline extractions in jsCode [#200]
- Support for js template literals [#214]
- Fixed tabs in PHP comments [#215]
## [4.6.2] - 2019-01-12
### Added
- New option `facade` in blade extractor to use a facade instead create a blade compiler [#197], [#198]
### Fixed
- Added php-7.3 to travis
- Added VueJS extractor method docblocks for IDEs [#191]
## [4.6.1] - 2018-08-27
### Fixed
- VueJS DOM parsing [#188]
- Javascript parser was unable to extract some functions [#187]
## [4.6.0] - 2018-06-26
### Added
- New extractor for VueJs [#178]
### Fixed
- Do not include empty translations containing the headers in the translator [#182]
- Test enhancement [#177]
## [4.5.0] - 2018-04-23
### Added
- Support for disabled translations
### Fixed
- Added php-7.2 to travis
- Fixed po tests on bigendian [#159]
- Improved comment estraction [#166]
- Fixed incorrect docs to dn__ function [#170]
- Ignored phpcs.xml file on export [#168]
- Improved `@method` docs in `Translations` [#175]
## [4.4.4] - 2018-02-21
### Fixed
- Changed the comment extraction to be compatible with gettext behaviour: the comment must be placed in the line preceding the function [#161]
### Security
- Validate eval input from plural forms [#156]
## [4.4.3] - 2017-08-09
### Fixed
- Handle `NULL` arguments on extract entries in php. For example `dn__(null, 'singular', 'plural')`.
- Fixed the `PhpCode` and `JsCode` extractors that didn't extract `dn__` and `dngettext` entries [#155].
- Fixed the `PhpCode` and `JsCode` extractors that didn't extract `dnpgettext` correctly.
## [4.4.2] - 2017-07-27
### Fixed
- Clone the translations in `Translations::mergeWith` to prevent that the translation is referenced in both places. [#152]
- Fixed escaped quotes in the javascript extractor [#154]
## [4.4.1] - 2017-05-20
### Fixed
- Fixed a bug where the options was not passed correctly to the merging Translations object [#147]
- Unified the plural behaviours between PHP gettext and Translator when the plural translation is unknown [#148]
- Removed the deprecated function `create_function()` and use `eval()` instead
## [4.4.0] - 2017-05-10
### Added
- New option `noLocation` to po generator, to omit the references [#143]
- New options `delimiter`, `enclosure` and `escape_char` to Csv and CsvDictionary extractors and generators [#145]
- Added the missing `dn__()` function [#146]
### Fixed
- Improved the code style including php_codesniffer in development
## 4.3.0 - 2017-03-04
### Added
- Added support for named placeholders (using `strtr`). For example:
```php
__('Hello :name', [':name' => 'World']);
```
- Added support for Twig v2
- New function `BaseTranslator::includeFunctions()` to include the functions file without register any translator
### Fixed
- Fixed a bug related with the javascript source extraction with single quotes
[#143]: https://github.com/oscarotero/Gettext/issues/143
[#145]: https://github.com/oscarotero/Gettext/issues/145
[#146]: https://github.com/oscarotero/Gettext/issues/146
[#147]: https://github.com/oscarotero/Gettext/issues/147
[#148]: https://github.com/oscarotero/Gettext/issues/148
[#152]: https://github.com/oscarotero/Gettext/issues/152
[#154]: https://github.com/oscarotero/Gettext/issues/154
[#155]: https://github.com/oscarotero/Gettext/issues/155
[#156]: https://github.com/oscarotero/Gettext/issues/156
[#159]: https://github.com/oscarotero/Gettext/issues/159
[#161]: https://github.com/oscarotero/Gettext/issues/161
[#166]: https://github.com/oscarotero/Gettext/issues/166
[#168]: https://github.com/oscarotero/Gettext/issues/168
[#170]: https://github.com/oscarotero/Gettext/issues/170
[#175]: https://github.com/oscarotero/Gettext/issues/175
[#177]: https://github.com/oscarotero/Gettext/issues/177
[#178]: https://github.com/oscarotero/Gettext/issues/178
[#182]: https://github.com/oscarotero/Gettext/issues/182
[#187]: https://github.com/oscarotero/Gettext/issues/187
[#188]: https://github.com/oscarotero/Gettext/issues/188
[#191]: https://github.com/oscarotero/Gettext/issues/191
[#197]: https://github.com/oscarotero/Gettext/issues/197
[#198]: https://github.com/oscarotero/Gettext/issues/198
[#200]: https://github.com/oscarotero/Gettext/issues/200
[#205]: https://github.com/oscarotero/Gettext/issues/205
[#213]: https://github.com/oscarotero/Gettext/issues/213
[#214]: https://github.com/oscarotero/Gettext/issues/214
[#215]: https://github.com/oscarotero/Gettext/issues/215
[#218]: https://github.com/oscarotero/Gettext/issues/218
[#221]: https://github.com/oscarotero/Gettext/issues/221
[#223]: https://github.com/oscarotero/Gettext/issues/223
[#224]: https://github.com/oscarotero/Gettext/issues/224
[#225]: https://github.com/oscarotero/Gettext/issues/225
[#226]: https://github.com/oscarotero/Gettext/issues/226
[#230]: https://github.com/oscarotero/Gettext/issues/230
[#231]: https://github.com/oscarotero/Gettext/issues/231
[#238]: https://github.com/oscarotero/Gettext/issues/238
[#242]: https://github.com/oscarotero/Gettext/issues/242
[#261]: https://github.com/oscarotero/Gettext/issues/261
[4.8.3]: https://github.com/oscarotero/Gettext/compare/v4.8.2...v4.8.3
[4.8.2]: https://github.com/oscarotero/Gettext/compare/v4.8.1...v4.8.2
[4.8.1]: https://github.com/oscarotero/Gettext/compare/v4.8.0...v4.8.1
[4.8.0]: https://github.com/oscarotero/Gettext/compare/v4.7.0...v4.8.0
[4.7.0]: https://github.com/oscarotero/Gettext/compare/v4.6.3...v4.7.0
[4.6.3]: https://github.com/oscarotero/Gettext/compare/v4.6.2...v4.6.3
[4.6.2]: https://github.com/oscarotero/Gettext/compare/v4.6.1...v4.6.2
[4.6.1]: https://github.com/oscarotero/Gettext/compare/v4.6.0...v4.6.1
[4.6.0]: https://github.com/oscarotero/Gettext/compare/v4.5.0...v4.6.0
[4.5.0]: https://github.com/oscarotero/Gettext/compare/v4.4.4...v4.5.0
[4.4.4]: https://github.com/oscarotero/Gettext/compare/v4.4.3...v4.4.4
[4.4.3]: https://github.com/oscarotero/Gettext/compare/v4.4.2...v4.4.3
[4.4.2]: https://github.com/oscarotero/Gettext/compare/v4.4.1...v4.4.2
[4.4.1]: https://github.com/oscarotero/Gettext/compare/v4.4.0...v4.4.1
[4.4.0]: https://github.com/oscarotero/Gettext/compare/v4.3.0...v4.4.0
+17
View File
@@ -0,0 +1,17 @@
Contributing to Gettext
=======================
Looking to contribute something to this library? Here's how you can help.
## Bugs
A bug is a demonstrable problem that is caused by the code in the repository. Good bug reports are extremely helpful thank you!
Please try to be as detailed as possible in your report. Include specific information about the environment version of PHP, version of gettext, etc, and steps required to reproduce the issue.
## Pull Requests
Good pull requests patches, improvements, new features are a fantastic help. New extractors or generator are welcome. Before create a pull request, please follow these instructions:
* The code must be PSR-2 compliant
* Write some tests
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Oscar Otero Marzoa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+425
View File
@@ -0,0 +1,425 @@
Gettext
=======
[![Build Status](https://travis-ci.org/oscarotero/Gettext.png?branch=master)](https://travis-ci.org/oscarotero/Gettext)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/oscarotero/Gettext/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/oscarotero/Gettext/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/gettext/gettext/v/stable.svg)](https://packagist.org/packages/gettext/gettext)
[![Total Downloads](https://poser.pugx.org/gettext/gettext/downloads.svg)](https://packagist.org/packages/gettext/gettext)
[![Monthly Downloads](https://poser.pugx.org/gettext/gettext/d/monthly.png)](https://packagist.org/packages/gettext/gettext)
[![License](https://poser.pugx.org/gettext/gettext/license.svg)](https://packagist.org/packages/gettext/gettext)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/496dc2a6-43be-4046-a283-f8370239dd47/big.png)](https://insight.sensiolabs.com/projects/496dc2a6-43be-4046-a283-f8370239dd47)
Created by Oscar Otero <http://oscarotero.com> <oom@oscarotero.com> (MIT License)
Gettext is a PHP (>=5.4) library to import/export/edit gettext from PO, MO, PHP, JS files, etc.
## Installation
With composer (recomended):
```
composer require gettext/gettext
```
If you don't use composer in your project, you have to download and place this package in a directory of your project. You need to install also [gettext/languages](https://github.com/mlocati/cldr-to-gettext-plural-rules). Then, include the autoloaders of both projects in any place of your php code:
```php
include_once "libs/gettext/src/autoloader.php";
include_once "libs/cldr-to-gettext-plural-rules/src/autoloader.php";
```
## Classes and functions
This package contains the following classes:
* `Gettext\Translation` - A translation definition
* `Gettext\Translations` - A collection of translations
* `Gettext\Extractors\*` - Import translations from various sources (po, mo, php, js, etc)
* `Gettext\Generators\*` - Export translations to various formats (po, mo, php, json, etc)
* `Gettext\Translator` - To use the translations in your php templates instead the [gettext extension](http://php.net/gettext)
* `Gettext\GettextTranslator` - To use the [gettext extension](http://php.net/gettext)
## Usage example
```php
use Gettext\Translations;
//import from a .po file:
$translations = Translations::fromPoFile('locales/gl.po');
//edit some translations:
$translation = $translations->find(null, 'apple');
if ($translation) {
$translation->setTranslation('Mazá');
}
//export to a php array:
$translations->toPhpArrayFile('locales/gl.php');
//and to a .mo file
$translations->toMoFile('Locale/gl/LC_MESSAGES/messages.mo');
```
If you want use this translations in your php templates without using the gettext extension:
```php
use Gettext\Translator;
//Create the translator instance
$t = new Translator();
//Load your translations (exported as PhpArray):
$t->loadTranslations('locales/gl.php');
//Use it:
echo $t->gettext('apple'); // "Mazá"
//If you want use global functions:
$t->register();
echo __('apple'); // "Mazá"
```
To use this translations with the gettext extension:
```php
use Gettext\GettextTranslator;
//Create the translator instance
$t = new GettextTranslator();
//Set the language and load the domain
$t->setLanguage('gl');
$t->loadDomain('messages', 'Locale');
//Use it:
echo $t->gettext('apple'); // "Mazá"
//Or use the gettext functions
echo gettext('apple'); // "Mazá"
//If you want use the global functions
$t->register();
echo __('apple'); // "Mazá"
//And use sprintf/strtr placeholders
echo __('Hello %s', 'world'); //Hello world
echo __('Hello {name}', ['{name}' => 'world']); //Hello world
```
The benefits of using the functions provided by this library (`__()` instead `_()` or `gettext()`) are:
* You are using the same functions, no matter whether the translations are provided by gettext extension or any other method.
* You can use variables easier because `sprintf` functionality is included. For example: `__('Hello %s', 'world')` instead `sprintf(_('Hello %s'), 'world')`.
* You can also use named placeholders if the second argument is an array. For example: `__('Hello %name%', ['%name%' => 'world'])` instead of `strtr(_('Hello %name%'), ['%name%' => 'world'])`.
## Translation
The `Gettext\Translation` class stores all information about a translation: the original text, the translated text, source references, comments, etc.
```php
// __construct($context, $original, $plural)
$translation = new Gettext\Translation('comments', 'One comment', '%s comments');
$translation->setTranslation('Un comentario');
$translation->setPluralTranslation('%s comentarios');
$translation->addReference('templates/comments/comment.php', 34);
$translation->addComment('To display the amount of comments in a post');
echo $translation->getContext(); // comments
echo $translation->getOriginal(); // One comment
echo $translation->getTranslation(); // Un comentario
// etc...
```
## Translations
The `Gettext\Translations` class stores a collection of translations:
```php
$translations = new Gettext\Translations();
//You can add new translations using the array syntax
$translations[] = new Gettext\Translation('comments', 'One comment', '%s comments');
//Or using the "insert" method
$insertedTranslation = $translations->insert('comments', 'One comment', '%s comments');
//Find a specific translation
$translation = $translations->find('comments', 'One comment');
//Edit headers, domain, etc
$translations->setHeader('Last-Translator', 'Oscar Otero');
$translations->setDomain('my-blog');
```
## Extractors
The extrators allows to fetch gettext values from any source. For example, to scan a .po file:
```php
$translations = new Gettext\Translations();
//From a file
Gettext\Extractors\Po::fromFile('locales/en.po', $translations);
//From a string
$string = file_get_contents('locales2/en.po');
Gettext\Extractors\Po::fromString($string, $translations);
```
The better way to use extractors is using the magic methods of `Gettext\Translations`:
```php
//Create a Translations instance using a po file
$translations = Gettext\Translations::fromPoFile('locales/en.po');
//Add more messages from other files
$translations->addFromPoFile('locales2/en.po');
```
The available extractors are the following:
Name | Description | Example
---- | ----------- | --------
**Blade** | Scans a Blade template (For laravel users). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/blade/input.php)
**Csv** | Gets the messages from csv. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Csv.csv)
**CsvDictionary** | Gets the messages from csv (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/CsvDictionary.csv)
**Jed** | Gets the messages from a json compatible with [Jed](http://slexaxton.github.com/Jed/). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Jed.json)
**JsCode** | Scans javascript code looking for all gettext functions (the same than PhpCode but for javascript). You can use [the javascript gettext-translator library](https://github.com/oscarotero/gettext-translator) | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/jscode/input.js)
**Json** | Gets the messages from json compatible with [gettext-translator](https://github.com/oscarotero/gettext-translator). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Json.json)
**JsonDictionary** | Gets the messages from a json (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/JsonDictionary.json)
**Mo** | Gets the messages from MO. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Mo.mo)
**PhpArray** | Gets the messages from a php file that returns an array. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/PhpArray.php)
**PhpCode** | Scans php code looking for all gettext functions (see `translator_functions.php`). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/phpcode/input.php)
**Po** | Gets the messages from PO. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Po.po)
**Twig** | To scan a Twig template. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/twig/input.php)
**Xliff** | Gets the messages from [xliff (2.0)](http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Xliff.xlf)
**Yaml** | Gets the messages from yaml. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Yaml.yml)
**YamlDictionary** | Gets the messages from a yaml (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/YamlDictionary.yml)
**VueJs** | Gets the messages from a VueJs template. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/vuejs/input.vue)
## Generators
The generators export a `Gettext\Translations` instance to any format (po, mo, array, etc).
```php
//Save to a file
Gettext\Generators\Po::toFile($translations, 'locales/en.po');
//Return as a string
$content = Gettext\Generators\Po::toString($translations);
file_put_contents('locales/en.po', $content);
```
Like extractors, the better way to use generators is using the magic methods of `Gettext\Translations`:
```php
//Extract messages from a php code file
$translations = Gettext\Translations::fromPhpCodeFile('templates/index.php');
//Export to a po file
$translations->toPoFile('locales/en.po');
//Export to a po string
$content = $translations->toPoString();
file_put_contents('locales/en.po', $content);
```
The available generators are the following:
Name | Description | Example
---- | ----------- | --------
**Csv** | Exports to csv. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Csv.csv)
**CsvDictionary** | Exports to csv (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/CsvDictionary.csv)
**Json** | Exports to json, compatible with [gettext-translator](https://github.com/oscarotero/gettext-translator). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Json.json)
**JsonDictionary** | Exports to json (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/JsonDictionary.json)
**Mo** | Exports to Mo. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Mo.mo)
**PhpArray** | Exports to php code that returns an array. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/PhpArray.php)
**Po** | Exports to Po. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Po.po)
**Jed** | Exports to json format compatible with [Jed](http://slexaxton.github.com/Jed/). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Jed.json)
**Xliff** | Exports to [xliff (2.0)](http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Xliff.xlf)
**Yaml** | Exports to yaml. | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/Yaml.yml)
**YamlDictionary** | Exports to yaml (without plurals and context). | [example](https://github.com/oscarotero/Gettext/blob/master/tests/assets/po/YamlDictionary.yml)
## Translator
The class `Gettext\Translator` implements the gettext functions in php. Useful if you don't have the native gettext extension for php or want to avoid problems with it. You can load the translations from a php array file or using a `Gettext\Translations` instance:
```php
use Gettext\Translator;
//Create a new instance of the translator
$t = new Translator();
//Load the translations using any of the following ways:
// 1. from php files (generated by Gettext\Extractors\PhpArray)
$t->loadTranslations('locales/gl.php');
// 2. using the array directly
$array = include 'locales/gl.php';
$t->loadTranslations($array);
// 3. using a Gettext\Translations instance (slower)
$translations = Gettext\Translations::fromPoFile('locales/gl.po');
$t->loadTranslations($translations);
//Now you can use it in your templates
echo $t->gettext('apple');
```
## GettextTranslator
The class `Gettext\GettextTranslator` uses the gettext extension. It's useful because combines the performance of using real gettext functions but with the same API than `Translator` class, so you can switch to one or other translator deppending of the environment without change code of your app.
```php
use Gettext\GettextTranslator;
//Create a new instance
$t = new GettextTranslator();
//It detects the environment variables to set the locale, but you can change it:
$t->setLanguage('gl');
//Load the domains:
$t->loadDomain('messages', 'project/Locale');
//this means you have the file "project/Locale/gl/LC_MESSAGES/messages.po"
//Now you can use it in your templates
echo $t->gettext('apple');
```
## Global functions
To ease the use of translations in your php templates, you can use the provided functions:
```php
//Register the translator to use the global functions
$t->register();
echo __('apple'); // it's the same than $t->gettext('apple');
```
You can scan the php files containing these functions and extract the values with the PhpCode extractor:
```html
<!-- index.php -->
<html>
<body>
<?= __('Hello world'); ?>
</body>
</html>
```
## Merge translations
To work with different translations you may want merge them in an unique file. There are two ways to do this:
The simplest way is adding new translations:
```php
use Gettext\Translations;
$translations = Translations::fromPoFile('my-file1.po');
$translations->addFromPoFile('my-file2.po');
```
A more advanced way is merge two `Translations` instances:
```php
use Gettext\Translations;
//Create a new Translations instances with our translations.
$translations1 = Translations::fromPoFile('my-file1.po');
$translations2 = Translations::fromPoFile('my-file2.po');
//Merge one inside other:
$translations1->mergeWith($translations2);
//Now translations1 has all values
```
The second argument of `mergeWith` defines how the merge will be done. Use the `Gettext\Merge` constants to configure the merging:
Constant | Description
--------- | -----------
`Merge::ADD` | Adds the translations from `$translations2` that are missing
`Merge::REMOVE` | Removes the translations missing in `$translations2`
`Merge::HEADERS_ADD` | Adds the headers from `$translations2` that are missing
`Merge::HEADERS_REMOVE` | Removes the headers missing in `$translations2`
`Merge::HEADERS_OVERRIDE` | Overrides the headers with the values of `$translations2`
`Merge::LANGUAGE_OVERRIDE` | Set the language defined in `$translations2`
`Merge::DOMAIN_OVERRIDE` | Set the domain defined in `$translations2`
`Merge::TRANSLATION_OVERRIDE` | Override the translation and plural translations with the value of `$translation2`
`Merge::COMMENTS_OURS` | Use only the comments of `$translation1`
`Merge::COMMENTS_THEIRS` | Use only the comments of `$translation2`
`Merge::EXTRACTED_COMMENTS_OURS` | Use only the extracted comments of `$translation1`
`Merge::EXTRACTED_COMMENTS_THEIRS` | Use only the extracted comments of `$translation2`
`Merge::FLAGS_OURS` | Use only the flags of `$translation1`
`Merge::FLAGS_THEIRS` | Use only the flags of `$translation2`
`Merge::REFERENCES_OURS` | Use only the references of `$translation1`
`Merge::REFERENCES_THEIRS` | Use only the references of `$translation2`
Example:
```php
use Gettext\Translations;
use Gettext\Merge;
//Scan the php code to find the latest gettext translations
$phpTranslations = Translations::fromPhpCodeFile('my-templates.php');
//Get the translations of the code that are stored in a po file
$poTranslations = Translations::fromPoFile('locale.po');
//Merge the translations from the po file using the references from `$phpTranslations`:
$translations->mergeWith($poTranslations, Merge::REFERENCES_OURS);
//Now save a po file with the result
$translations->toPoFile('locale.po');
```
Note, if the second argument is not defined, the default value is `Merge::DEFAULTS` that's equivalent to `Merge::ADD | Merge::HEADERS_ADD`.
## Use from CLI
There's a Robo task to use this library from the command line interface: https://github.com/oscarotero/GettextRobo
## Use in the browser
If you want to use your translations in the browser, there's a javascript translator: https://github.com/oscarotero/gettext-translator
## Third party packages
Twig integration:
* [jaimeperez/twig-configurable-i18n](https://packagist.org/packages/jaimeperez/twig-configurable-i18n)
* [cemerson/translator-twig-extension](https://packagist.org/packages/cemerson/translator-twig-extension)
Framework integration:
* [Laravel 5](https://packagist.org/packages/eusonlito/laravel-gettext)
* [CakePHP 3](https://packagist.org/packages/k1low/po)
* [Symfony 2](https://packagist.org/packages/mablae/gettext-bundle)
[add your package](https://github.com/oscarotero/Gettext/issues/new)
## Contributors
Thanks to all [contributors](https://github.com/oscarotero/Gettext/graphs/contributors) specially to [@mlocati](https://github.com/mlocati).
## Donations
If this library is useful for you, consider to donate to the author.
[Buy me a beer :beer:](https://www.paypal.me/oscarotero)
Thanks in advance!
+54
View File
@@ -0,0 +1,54 @@
{
"name": "gettext/gettext",
"type": "library",
"description": "PHP gettext manager",
"keywords": ["js", "gettext", "i18n", "translation", "po", "mo"],
"homepage": "https://github.com/oscarotero/Gettext",
"license": "MIT",
"authors": [
{
"name": "Oscar Otero",
"email": "oom@oscarotero.com",
"homepage": "http://oscarotero.com",
"role": "Developer"
}
],
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/oscarotero/Gettext/issues"
},
"require": {
"php": ">=5.4.0",
"gettext/languages": "^2.3"
},
"require-dev": {
"illuminate/view": "*",
"twig/twig": "^1.31|^2.0",
"twig/extensions": "*",
"symfony/yaml": "~2",
"phpunit/phpunit": "^4.8|^5.7|^6.5",
"squizlabs/php_codesniffer": "^3.0"
},
"suggest": {
"illuminate/view": "Is necessary if you want to use the Blade extractor",
"twig/twig": "Is necessary if you want to use the Twig extractor",
"twig/extensions": "Is necessary if you want to use the Twig extractor",
"symfony/yaml": "Is necessary if you want to use the Yaml extractor/generator"
},
"autoload": {
"psr-4": {
"Gettext\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Gettext\\Tests\\": "tests"
}
},
"scripts": {
"test": [
"phpunit",
"phpcs"
]
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Gettext;
abstract class BaseTranslator implements TranslatorInterface
{
/** @var TranslatorInterface */
public static $current;
/**
* @see TranslatorInterface
*/
public function noop($original)
{
return $original;
}
/**
* @see TranslatorInterface
*/
public function register()
{
$previous = static::$current;
static::$current = $this;
static::includeFunctions();
return $previous;
}
/**
* Include the gettext functions
*/
public static function includeFunctions()
{
include_once __DIR__.'/translator_functions.php';
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Illuminate\Filesystem\Filesystem;
use Illuminate\View\Compilers\BladeCompiler;
/**
* Class to get gettext strings from blade.php files returning arrays.
*/
class Blade extends Extractor implements ExtractorInterface
{
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
if (empty($options['facade'])) {
$cachePath = empty($options['cachePath']) ? sys_get_temp_dir() : $options['cachePath'];
$bladeCompiler = new BladeCompiler(new Filesystem(), $cachePath);
if (method_exists($bladeCompiler, 'withoutComponentTags')) {
$bladeCompiler->withoutComponentTags();
}
$string = $bladeCompiler->compileString($string);
} else {
$string = $options['facade']::compileString($string);
}
PhpCode::fromString($string, $translations, $options);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Utils\HeadersExtractorTrait;
use Gettext\Utils\CsvTrait;
/**
* Class to get gettext strings from csv.
*/
class Csv extends Extractor implements ExtractorInterface
{
use HeadersExtractorTrait;
use CsvTrait;
public static $options = [
'delimiter' => ",",
'enclosure' => '"',
'escape_char' => "\\"
];
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$options += static::$options;
$handle = fopen('php://memory', 'w');
fputs($handle, $string);
rewind($handle);
while ($row = static::fgetcsv($handle, $options)) {
$context = array_shift($row);
$original = array_shift($row);
if ($context === '' && $original === '') {
static::extractHeaders(array_shift($row), $translations);
continue;
}
$translation = $translations->insert($context, $original);
if (!empty($row)) {
$translation->setTranslation(array_shift($row));
$translation->setPluralTranslations($row);
}
}
fclose($handle);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Utils\HeadersExtractorTrait;
use Gettext\Utils\CsvTrait;
/**
* Class to get gettext strings from csv.
*/
class CsvDictionary extends Extractor implements ExtractorInterface
{
use HeadersExtractorTrait;
use CsvTrait;
public static $options = [
'delimiter' => ",",
'enclosure' => '"',
'escape_char' => "\\"
];
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$options += static::$options;
$handle = fopen('php://memory', 'w');
fputs($handle, $string);
rewind($handle);
while ($row = static::fgetcsv($handle, $options)) {
list($original, $translation) = $row + ['', ''];
if ($original === '') {
static::extractHeaders($translation, $translations);
continue;
}
$translations->insert(null, $original)->setTranslation($translation);
}
fclose($handle);
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace Gettext\Extractors;
use Exception;
use InvalidArgumentException;
use Gettext\Translations;
abstract class Extractor implements ExtractorInterface
{
/**
* {@inheritdoc}
*/
public static function fromFile($file, Translations $translations, array $options = [])
{
foreach (static::getFiles($file) as $file) {
$options['file'] = $file;
static::fromString(static::readFile($file), $translations, $options);
}
}
/**
* Checks and returns all files.
*
* @param string|array $file The file/s
*
* @return array The file paths
*/
protected static function getFiles($file)
{
if (empty($file)) {
throw new InvalidArgumentException('There is not any file defined');
}
if (is_string($file)) {
if (!is_file($file)) {
throw new InvalidArgumentException("'$file' is not a valid file");
}
if (!is_readable($file)) {
throw new InvalidArgumentException("'$file' is not a readable file");
}
return [$file];
}
if (is_array($file)) {
$files = [];
foreach ($file as $f) {
$files = array_merge($files, static::getFiles($f));
}
return $files;
}
throw new InvalidArgumentException('The first argument must be string or array');
}
/**
* Reads and returns the content of a file.
*
* @param string $file
*
* @return string
*/
protected static function readFile($file)
{
$length = filesize($file);
if (!($fd = fopen($file, 'rb'))) {
throw new Exception("Cannot read the file '$file', probably permissions");
}
$content = $length ? fread($fd, $length) : '';
fclose($fd);
return $content;
}
}
@@ -0,0 +1,26 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
interface ExtractorInterface
{
/**
* Extract the translations from a file.
*
* @param array|string $file A path of a file or files
* @param Translations $translations The translations instance to append the new translations.
* @param array $options
*/
public static function fromFile($file, Translations $translations, array $options = []);
/**
* Parses a string and append the translations found in the Translations instance.
*
* @param string $string
* @param Translations $translations
* @param array $options
*/
public static function fromString($string, Translations $translations, array $options = []);
}
@@ -0,0 +1,28 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
interface ExtractorMultiInterface
{
/**
* Parses a string and append the translations found in the Translations instance.
* Allows scanning for multiple domains at a time (each Translation has to have a different domain)
*
* @param string $string
* @param Translations[] $translations
* @param array $options
*/
public static function fromStringMultiple($string, array $translations, array $options = []);
/**
* Parses a string and append the translations found in the Translations instance.
* Allows scanning for multiple domains at a time (each Translation has to have a different domain)
*
* @param $file
* @param Translations[] $translations
* @param array $options
*/
public static function fromFileMultiple($file, array $translations, array $options = []);
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
/**
* Class to get gettext strings from json files.
*/
class Jed extends Extractor implements ExtractorInterface
{
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
static::extract(json_decode($string, true), $translations);
}
/**
* Handle an array of translations and append to the Translations instance.
*
* @param array $content
* @param Translations $translations
*/
public static function extract(array $content, Translations $translations)
{
$messages = current($content);
$headers = isset($messages['']) ? $messages[''] : null;
unset($messages['']);
if (!empty($headers['domain'])) {
$translations->setDomain($headers['domain']);
}
if (!empty($headers['lang'])) {
$translations->setLanguage($headers['lang']);
}
if (!empty($headers['plural-forms'])) {
$translations->setHeader(Translations::HEADER_PLURAL, $headers['plural-forms']);
}
$context_glue = '\u0004';
foreach ($messages as $key => $translation) {
$key = explode($context_glue, $key);
$context = isset($key[1]) ? array_shift($key) : '';
$translations->insert($context, array_shift($key))
->setTranslation(array_shift($translation))
->setPluralTranslations($translation);
}
}
}
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace Gettext\Extractors;
use Exception;
use Gettext\Translations;
use Gettext\Utils\FunctionsScanner;
/**
* Class to get gettext strings from javascript files.
*/
class JsCode extends Extractor implements ExtractorInterface, ExtractorMultiInterface
{
public static $options = [
'constants' => [],
'functions' => [
'gettext' => 'gettext',
'__' => 'gettext',
'ngettext' => 'ngettext',
'n__' => 'ngettext',
'pgettext' => 'pgettext',
'p__' => 'pgettext',
'dgettext' => 'dgettext',
'd__' => 'dgettext',
'dngettext' => 'dngettext',
'dn__' => 'dngettext',
'dpgettext' => 'dpgettext',
'dp__' => 'dpgettext',
'npgettext' => 'npgettext',
'np__' => 'npgettext',
'dnpgettext' => 'dnpgettext',
'dnp__' => 'dnpgettext',
'noop' => 'noop',
'noop__' => 'noop',
],
];
protected static $functionsScannerClass = 'Gettext\Utils\JsFunctionsScanner';
/**
* @inheritdoc
* @throws Exception
*/
public static function fromString($string, Translations $translations, array $options = [])
{
static::fromStringMultiple($string, [$translations], $options);
}
/**
* @inheritDoc
* @throws Exception
*/
public static function fromStringMultiple($string, array $translations, array $options = [])
{
$options += static::$options;
/** @var FunctionsScanner $functions */
$functions = new static::$functionsScannerClass($string);
$functions->saveGettextFunctions($translations, $options);
}
/**
* @inheritDoc
* @throws Exception
*/
public static function fromFileMultiple($file, array $translations, array $options = [])
{
foreach (static::getFiles($file) as $file) {
$options['file'] = $file;
static::fromStringMultiple(static::readFile($file), $translations, $options);
}
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Utils\MultidimensionalArrayTrait;
/**
* Class to get gettext strings from json.
*/
class Json extends Extractor implements ExtractorInterface
{
use MultidimensionalArrayTrait;
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$messages = json_decode($string, true);
if (is_array($messages)) {
static::fromArray($messages, $translations);
}
}
}
@@ -0,0 +1,26 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Utils\DictionaryTrait;
/**
* Class to get gettext strings from plain json.
*/
class JsonDictionary extends Extractor implements ExtractorInterface
{
use DictionaryTrait;
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$messages = json_decode($string, true);
if (is_array($messages)) {
static::fromArray($messages, $translations);
}
}
}
+131
View File
@@ -0,0 +1,131 @@
<?php
namespace Gettext\Extractors;
use Exception;
use Gettext\Translations;
use Gettext\Utils\StringReader;
/**
* Class to get gettext strings from .mo files.
*/
class Mo extends Extractor implements ExtractorInterface
{
const MAGIC1 = -1794895138;
const MAGIC2 = -569244523;
const MAGIC3 = 2500072158;
protected static $stringReaderClass = 'Gettext\Utils\StringReader';
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
/** @var StringReader $stream */
$stream = new static::$stringReaderClass($string);
$magic = static::readInt($stream, 'V');
if (($magic === static::MAGIC1) || ($magic === static::MAGIC3)) { //to make sure it works for 64-bit platforms
$byteOrder = 'V'; //low endian
} elseif ($magic === (static::MAGIC2 & 0xFFFFFFFF)) {
$byteOrder = 'N'; //big endian
} else {
throw new Exception('Not MO file');
}
static::readInt($stream, $byteOrder);
$total = static::readInt($stream, $byteOrder); //total string count
$originals = static::readInt($stream, $byteOrder); //offset of original table
$tran = static::readInt($stream, $byteOrder); //offset of translation table
$stream->seekto($originals);
$table_originals = static::readIntArray($stream, $byteOrder, $total * 2);
$stream->seekto($tran);
$table_translations = static::readIntArray($stream, $byteOrder, $total * 2);
for ($i = 0; $i < $total; ++$i) {
$next = $i * 2;
$stream->seekto($table_originals[$next + 2]);
$original = $stream->read($table_originals[$next + 1]);
$stream->seekto($table_translations[$next + 2]);
$translated = $stream->read($table_translations[$next + 1]);
if ($original === '') {
// Headers
foreach (explode("\n", $translated) as $headerLine) {
if ($headerLine === '') {
continue;
}
$headerChunks = preg_split('/:\s*/', $headerLine, 2);
$translations->setHeader($headerChunks[0], isset($headerChunks[1]) ? $headerChunks[1] : '');
}
continue;
}
$chunks = explode("\x04", $original, 2);
if (isset($chunks[1])) {
$context = $chunks[0];
$original = $chunks[1];
} else {
$context = '';
}
$chunks = explode("\x00", $original, 2);
if (isset($chunks[1])) {
$original = $chunks[0];
$plural = $chunks[1];
} else {
$plural = '';
}
$translation = $translations->insert($context, $original, $plural);
if ($translated === '') {
continue;
}
if ($plural === '') {
$translation->setTranslation($translated);
continue;
}
$v = explode("\x00", $translated);
$translation->setTranslation(array_shift($v));
$translation->setPluralTranslations($v);
}
}
/**
* @param StringReader $stream
* @param string $byteOrder
*/
protected static function readInt(StringReader $stream, $byteOrder)
{
if (($read = $stream->read(4)) === false) {
return false;
}
$read = unpack($byteOrder, $read);
return array_shift($read);
}
/**
* @param StringReader $stream
* @param string $byteOrder
* @param int $count
*/
protected static function readIntArray(StringReader $stream, $byteOrder, $count)
{
return unpack($byteOrder.$count, $stream->read(4 * $count));
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Gettext\Extractors;
use BadMethodCallException;
use Gettext\Translations;
use Gettext\Utils\MultidimensionalArrayTrait;
/**
* Class to get gettext strings from php files returning arrays.
*/
class PhpArray extends Extractor implements ExtractorInterface
{
use MultidimensionalArrayTrait;
/**
* {@inheritdoc}
*/
public static function fromFile($file, Translations $translations, array $options = [])
{
foreach (static::getFiles($file) as $file) {
static::fromArray(include($file), $translations);
}
}
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
throw new BadMethodCallException('PhpArray::fromString() cannot be called. Use PhpArray::fromFile()');
}
}
+170
View File
@@ -0,0 +1,170 @@
<?php
namespace Gettext\Extractors;
use Exception;
use Gettext\Translations;
use Gettext\Utils\FunctionsScanner;
/**
* Class to get gettext strings from php files returning arrays.
*/
class PhpCode extends Extractor implements ExtractorInterface, ExtractorMultiInterface
{
public static $options = [
// - false: to not extract comments
// - empty string: to extract all comments
// - non-empty string: to extract comments that start with that string
// - array with strings to extract comments format.
'extractComments' => false,
'constants' => [],
'functions' => [
'gettext' => 'gettext',
'__' => 'gettext',
'ngettext' => 'ngettext',
'n__' => 'ngettext',
'pgettext' => 'pgettext',
'p__' => 'pgettext',
'dgettext' => 'dgettext',
'd__' => 'dgettext',
'dngettext' => 'dngettext',
'dn__' => 'dngettext',
'dpgettext' => 'dpgettext',
'dp__' => 'dpgettext',
'npgettext' => 'npgettext',
'np__' => 'npgettext',
'dnpgettext' => 'dnpgettext',
'dnp__' => 'dnpgettext',
'noop' => 'noop',
'noop__' => 'noop',
],
];
protected static $functionsScannerClass = 'Gettext\Utils\PhpFunctionsScanner';
/**
* {@inheritdoc}
* @throws Exception
*/
public static function fromString($string, Translations $translations, array $options = [])
{
static::fromStringMultiple($string, [$translations], $options);
}
/**
* @inheritDoc
* @throws Exception
*/
public static function fromStringMultiple($string, array $translations, array $options = [])
{
$options += static::$options;
/** @var FunctionsScanner $functions */
$functions = new static::$functionsScannerClass($string);
if ($options['extractComments'] !== false) {
$functions->enableCommentsExtraction($options['extractComments']);
}
$functions->saveGettextFunctions($translations, $options);
}
/**
* @inheritDoc
*/
public static function fromFileMultiple($file, array $translations, array $options = [])
{
foreach (static::getFiles($file) as $file) {
$options['file'] = $file;
static::fromStringMultiple(static::readFile($file), $translations, $options);
}
}
/**
* Decodes a T_CONSTANT_ENCAPSED_STRING string.
*
* @param string $value
*
* @return string
*/
public static function convertString($value)
{
if (strpos($value, '\\') === false) {
return substr($value, 1, -1);
}
if ($value[0] === "'") {
return strtr(substr($value, 1, -1), ['\\\\' => '\\', '\\\'' => '\'']);
}
$value = substr($value, 1, -1);
return preg_replace_callback(
'/\\\(n|r|t|v|e|f|\$|"|\\\|x[0-9A-Fa-f]{1,2}|u{[0-9a-f]{1,6}}|[0-7]{1,3})/',
function ($match) {
switch ($match[1][0]) {
case 'n':
return "\n";
case 'r':
return "\r";
case 't':
return "\t";
case 'v':
return "\v";
case 'e':
return "\e";
case 'f':
return "\f";
case '$':
return '$';
case '"':
return '"';
case '\\':
return '\\';
case 'x':
return chr(hexdec(substr($match[1], 1)));
case 'u':
return static::unicodeChar(hexdec(substr($match[1], 1)));
default:
return chr(octdec($match[1]));
}
},
$value
);
}
/**
* @param $dec
* @return string|null
* @see http://php.net/manual/en/function.chr.php#118804
*/
protected static function unicodeChar($dec)
{
if ($dec < 0x80) {
return chr($dec);
}
if ($dec < 0x0800) {
return chr(0xC0 + ($dec >> 6))
. chr(0x80 + ($dec & 0x3f));
}
if ($dec < 0x010000) {
return chr(0xE0 + ($dec >> 12))
. chr(0x80 + (($dec >> 6) & 0x3f))
. chr(0x80 + ($dec & 0x3f));
}
if ($dec < 0x200000) {
return chr(0xF0 + ($dec >> 18))
. chr(0x80 + (($dec >> 12) & 0x3f))
. chr(0x80 + (($dec >> 6) & 0x3f))
. chr(0x80 + ($dec & 0x3f));
}
return null;
}
}
+211
View File
@@ -0,0 +1,211 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Translation;
use Gettext\Utils\HeadersExtractorTrait;
/**
* Class to get gettext strings from php files returning arrays.
*/
class Po extends Extractor implements ExtractorInterface
{
use HeadersExtractorTrait;
/**
* Parses a .po file and append the translations found in the Translations instance.
*
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$lines = explode("\n", $string);
$i = 0;
$translation = $translations->createNewTranslation('', '');
for ($n = count($lines); $i < $n; ++$i) {
$line = trim($lines[$i]);
$line = static::fixMultiLines($line, $lines, $i);
if ($line === '') {
if ($translation->is('', '')) {
static::extractHeaders($translation->getTranslation(), $translations);
} elseif ($translation->hasOriginal()) {
$translations[] = $translation;
}
$translation = $translations->createNewTranslation('', '');
continue;
}
$splitLine = preg_split('/\s+/', $line, 2);
$key = $splitLine[0];
$data = isset($splitLine[1]) ? $splitLine[1] : '';
if ($key === '#~') {
$translation->setDisabled(true);
$splitLine = preg_split('/\s+/', $data, 2);
$key = $splitLine[0];
$data = isset($splitLine[1]) ? $splitLine[1] : '';
}
switch ($key) {
case '#':
$translation->addComment($data);
$append = null;
break;
case '#.':
$translation->addExtractedComment($data);
$append = null;
break;
case '#,':
foreach (array_map('trim', explode(',', trim($data))) as $value) {
$translation->addFlag($value);
}
$append = null;
break;
case '#:':
foreach (preg_split('/\s+/', trim($data)) as $value) {
if (preg_match('/^(.+)(:(\d*))?$/U', $value, $matches)) {
$translation->addReference($matches[1], isset($matches[3]) ? $matches[3] : null);
}
}
$append = null;
break;
case 'msgctxt':
$translation = $translation->getClone(static::convertString($data));
$append = 'Context';
break;
case 'msgid':
$translation = $translation->getClone(null, static::convertString($data));
$append = 'Original';
break;
case 'msgid_plural':
$translation->setPlural(static::convertString($data));
$append = 'Plural';
break;
case 'msgstr':
case 'msgstr[0]':
$translation->setTranslation(static::convertString($data));
$append = 'Translation';
break;
case 'msgstr[1]':
$translation->setPluralTranslations([static::convertString($data)]);
$append = 'PluralTranslation';
break;
default:
if (strpos($key, 'msgstr[') === 0) {
$p = $translation->getPluralTranslations();
$p[] = static::convertString($data);
$translation->setPluralTranslations($p);
$append = 'PluralTranslation';
break;
}
if (isset($append)) {
if ($append === 'Context') {
$translation = $translation->getClone($translation->getContext()
."\n"
.static::convertString($data));
break;
}
if ($append === 'Original') {
$translation = $translation->getClone(null, $translation->getOriginal()
."\n"
.static::convertString($data));
break;
}
if ($append === 'PluralTranslation') {
$p = $translation->getPluralTranslations();
$p[] = array_pop($p)."\n".static::convertString($data);
$translation->setPluralTranslations($p);
break;
}
$getMethod = 'get'.$append;
$setMethod = 'set'.$append;
$translation->$setMethod($translation->$getMethod()."\n".static::convertString($data));
}
break;
}
}
if ($translation->hasOriginal() && !in_array($translation, iterator_to_array($translations))) {
$translations[] = $translation;
}
}
/**
* Gets one string from multiline strings.
*
* @param string $line
* @param array $lines
* @param int &$i
*
* @return string
*/
protected static function fixMultiLines($line, array $lines, &$i)
{
for ($j = $i, $t = count($lines); $j < $t; ++$j) {
if (substr($line, -1, 1) == '"'
&& isset($lines[$j + 1])
&& substr(trim($lines[$j + 1]), 0, 1) == '"'
) {
$line = substr($line, 0, -1).substr(trim($lines[$j + 1]), 1);
} else {
$i = $j;
break;
}
}
return $line;
}
/**
* Convert a string from its PO representation.
*
* @param string $value
*
* @return string
*/
public static function convertString($value)
{
if (!$value) {
return '';
}
if ($value[0] === '"') {
$value = substr($value, 1, -1);
}
return strtr(
$value,
[
'\\\\' => '\\',
'\\a' => "\x07",
'\\b' => "\x08",
'\\t' => "\t",
'\\n' => "\n",
'\\v' => "\x0b",
'\\f' => "\x0c",
'\\r' => "\r",
'\\"' => '"',
]
);
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Twig_Loader_Array;
use Twig_Environment;
use Twig_Source;
use Twig_Extensions_Extension_I18n;
/**
* Class to get gettext strings from twig files returning arrays.
*/
class Twig extends Extractor implements ExtractorInterface
{
public static $options = [
'extractComments' => 'notes:',
'twig' => null,
];
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$options += static::$options;
$twig = $options['twig'] ?: static::createTwig();
PhpCode::fromString($twig->compileSource(new Twig_Source($string, '')), $translations, $options);
}
/**
* Returns a Twig instance.
*
* @return Twig_Environment
*/
protected static function createTwig()
{
$twig = new Twig_Environment(new Twig_Loader_Array(['' => '']));
$twig->addExtension(new Twig_Extensions_Extension_I18n());
return static::$options['twig'] = $twig;
}
}
+423
View File
@@ -0,0 +1,423 @@
<?php
/** @noinspection PhpComposerExtensionStubsInspection */
namespace Gettext\Extractors;
use DOMAttr;
use DOMDocument;
use DOMElement;
use DOMNode;
use Exception;
use Gettext\Translations;
use Gettext\Utils\FunctionsScanner;
/**
* Class to get gettext strings from VueJS template files.
*/
class VueJs extends Extractor implements ExtractorInterface, ExtractorMultiInterface
{
public static $options = [
'constants' => [],
'functions' => [
'gettext' => 'gettext',
'__' => 'gettext',
'ngettext' => 'ngettext',
'n__' => 'ngettext',
'pgettext' => 'pgettext',
'p__' => 'pgettext',
'dgettext' => 'dgettext',
'd__' => 'dgettext',
'dngettext' => 'dngettext',
'dn__' => 'dngettext',
'dpgettext' => 'dpgettext',
'dp__' => 'dpgettext',
'npgettext' => 'npgettext',
'np__' => 'npgettext',
'dnpgettext' => 'dnpgettext',
'dnp__' => 'dnpgettext',
'noop' => 'noop',
'noop__' => 'noop',
],
];
protected static $functionsScannerClass = 'Gettext\Utils\JsFunctionsScanner';
/**
* @inheritDoc
* @throws Exception
*/
public static function fromFileMultiple($file, array $translations, array $options = [])
{
foreach (static::getFiles($file) as $file) {
$options['file'] = $file;
static::fromStringMultiple(static::readFile($file), $translations, $options);
}
}
/**
* @inheritdoc
* @throws Exception
*/
public static function fromString($string, Translations $translations, array $options = [])
{
static::fromStringMultiple($string, [$translations], $options);
}
/**
* @inheritDoc
* @throws Exception
*/
public static function fromStringMultiple($string, array $translations, array $options = [])
{
$options += static::$options;
$options += [
// HTML attribute prefixes we parse as JS which could contain translations (are JS expressions)
'attributePrefixes' => [
':',
'v-bind:',
'v-on:',
'v-text',
],
// HTML Tags to parse
'tagNames' => [
'translate',
],
// HTML tags to parse when attribute exists
'tagAttributes' => [
'v-translate',
],
// Comments
'commentAttributes' => [
'translate-comment',
],
'contextAttributes' => [
'translate-context',
],
// Attribute with plural content
'pluralAttributes' => [
'translate-plural',
],
];
// Ok, this is the weirdest hack, but let me explain:
// On Linux (Mac is fine), when converting HTML to DOM, new lines get trimmed after the first tag.
// So if there are new lines between <template> and next element, they are lost
// So we insert a "." which is a text node, and it will prevent that newlines are stripped between elements.
// Same thing happens between template and script tag.
$string = str_replace('<template>', '<template>.', $string);
$string = str_replace('</template>', '</template>.', $string);
// Normalize newlines
$string = str_replace(["\r\n", "\n\r", "\r"], "\n", $string);
// VueJS files are valid HTML files, we will operate with the DOM here
$dom = static::convertHtmlToDom($string);
$script = static::extractScriptTag($string);
// Parse the script part as a regular JS code
if ($script) {
$scriptLineNumber = $dom->getElementsByTagName('script')->item(0)->getLineNo();
static::getScriptTranslationsFromString(
$script,
$translations,
$options,
$scriptLineNumber - 1
);
}
// Template part is parsed separately, all variables will be extracted
// and handled as a regular JS code
$template = $dom->getElementsByTagName('template')->item(0);
if ($template) {
static::getTemplateTranslations(
$template,
$translations,
$options,
$template->getLineNo() - 1
);
}
}
/**
* Extracts script tag contents using regex instead of DOM operations.
* If we parse using DOM, some contents may change, for example, tags within strings will be stripped
*
* @param $string
* @return bool|string
*/
protected static function extractScriptTag($string)
{
if (preg_match('#<\s*?script\b[^>]*>(.*?)</script\b[^>]*>#s', $string, $matches)) {
return $matches[1];
}
return '';
}
/**
* @param string $html
* @return DOMDocument
*/
protected static function convertHtmlToDom($html)
{
$dom = new DOMDocument;
libxml_use_internal_errors(true);
// Prepend xml encoding so DOMDocument document handles UTF8 correctly.
// Assuming that vue template files will not have any xml encoding tags, because duplicate tags may be ignored.
$dom->loadHTML('<?xml encoding="utf-8"?>' . $html);
libxml_clear_errors();
return $dom;
}
/**
* Extract translations from script part
*
* @param string $scriptContents Only script tag contents, not the whole template
* @param Translations|Translations[] $translations One or multiple domain Translation objects
* @param array $options
* @param int $lineOffset Number of lines the script is offset in the vue template file
* @throws Exception
*/
protected static function getScriptTranslationsFromString(
$scriptContents,
$translations,
array $options = [],
$lineOffset = 0
) {
/** @var FunctionsScanner $functions */
$functions = new static::$functionsScannerClass($scriptContents);
$options['lineOffset'] = $lineOffset;
$functions->saveGettextFunctions($translations, $options);
}
/**
* Parse template to extract all translations (element content and dynamic element attributes)
*
* @param DOMNode $dom
* @param Translations|Translations[] $translations One or multiple domain Translation objects
* @param array $options
* @param int $lineOffset Line number where the template part starts in the vue file
* @throws Exception
*/
protected static function getTemplateTranslations(
DOMNode $dom,
$translations,
array $options,
$lineOffset = 0
) {
// Build a JS string from all template attribute expressions
$fakeAttributeJs = static::getTemplateAttributeFakeJs($options, $dom);
// 1 line offset is necessary because parent template element was ignored when converting to DOM
static::getScriptTranslationsFromString($fakeAttributeJs, $translations, $options, $lineOffset);
// Build a JS string from template element content expressions
$fakeTemplateJs = static::getTemplateFakeJs($dom);
static::getScriptTranslationsFromString($fakeTemplateJs, $translations, $options, $lineOffset);
static::getTagTranslations($options, $dom, $translations);
}
/**
* @param array $options
* @param DOMNode $dom
* @param Translations|Translations[] $translations
*/
protected static function getTagTranslations(array $options, DOMNode $dom, $translations)
{
// Since tag scanning does not support domains, we always use the first translation given
$translations = is_array($translations) ? reset($translations) : $translations;
$children = $dom->childNodes;
for ($i = 0; $i < $children->length; $i++) {
$node = $children->item($i);
if (!($node instanceof DOMElement)) {
continue;
}
$translatable = false;
if (in_array($node->tagName, $options['tagNames'], true)) {
$translatable = true;
}
$attrList = $node->attributes;
$context = null;
$plural = "";
$comment = null;
for ($j = 0; $j < $attrList->length; $j++) {
/** @var DOMAttr $domAttr */
$domAttr = $attrList->item($j);
// Check if this is a dynamic vue attribute
if (in_array($domAttr->name, $options['tagAttributes'])) {
$translatable = true;
}
if (in_array($domAttr->name, $options['contextAttributes'])) {
$context = $domAttr->value;
}
if (in_array($domAttr->name, $options['pluralAttributes'])) {
$plural = $domAttr->value;
}
if (in_array($domAttr->name, $options['commentAttributes'])) {
$comment = $domAttr->value;
}
}
if ($translatable) {
$translation = $translations->insert($context, trim($node->textContent), $plural);
$translation->addReference($options['file'], $node->getLineNo());
if ($comment) {
$translation->addExtractedComment($comment);
}
}
if ($node->hasChildNodes()) {
static::getTagTranslations($options, $node, $translations);
}
}
}
/**
* Extract JS expressions from element attribute bindings (excluding text within elements)
* For example: <span :title="__('extract this')"> skip element content </span>
*
* @param array $options
* @param DOMNode $dom
* @return string JS code
*/
protected static function getTemplateAttributeFakeJs(array $options, DOMNode $dom)
{
$expressionsByLine = static::getVueAttributeExpressions($options['attributePrefixes'], $dom);
if (empty($expressionsByLine)) {
return '';
}
$maxLines = max(array_keys($expressionsByLine));
$fakeJs = '';
for ($line = 1; $line <= $maxLines; $line++) {
if (isset($expressionsByLine[$line])) {
$fakeJs .= implode("; ", $expressionsByLine[$line]);
}
$fakeJs .= "\n";
}
return $fakeJs;
}
/**
* Loop DOM element recursively and parse out all dynamic vue attributes which are basically JS expressions
*
* @param array $attributePrefixes List of attribute prefixes we parse as JS (may contain translations)
* @param DOMNode $dom
* @param array $expressionByLine [lineNumber => [jsExpression, ..], ..]
* @return array [lineNumber => [jsExpression, ..], ..]
*/
protected static function getVueAttributeExpressions(
array $attributePrefixes,
DOMNode $dom,
array &$expressionByLine = []
) {
$children = $dom->childNodes;
for ($i = 0; $i < $children->length; $i++) {
$node = $children->item($i);
if (!($node instanceof DOMElement)) {
continue;
}
$attrList = $node->attributes;
for ($j = 0; $j < $attrList->length; $j++) {
/** @var DOMAttr $domAttr */
$domAttr = $attrList->item($j);
// Check if this is a dynamic vue attribute
if (static::isAttributeMatching($domAttr->name, $attributePrefixes)) {
$line = $domAttr->getLineNo();
$expressionByLine += [$line => []];
$expressionByLine[$line][] = $domAttr->value;
}
}
if ($node->hasChildNodes()) {
$expressionByLine = static::getVueAttributeExpressions($attributePrefixes, $node, $expressionByLine);
}
}
return $expressionByLine;
}
/**
* Check if this attribute name should be parsed for translations
*
* @param string $attributeName
* @param string[] $attributePrefixes
* @return bool
*/
protected static function isAttributeMatching($attributeName, $attributePrefixes)
{
foreach ($attributePrefixes as $prefix) {
if (strpos($attributeName, $prefix) === 0) {
return true;
}
}
return false;
}
/**
* Extract JS expressions from within template elements (excluding attributes)
* For example: <span :title="skip attributes"> {{__("extract element content")}} </span>
*
* @param DOMNode $dom
* @return string JS code
*/
protected static function getTemplateFakeJs(DOMNode $dom)
{
$fakeJs = '';
$lines = explode("\n", $dom->textContent);
// Build a fake JS file from template by extracting JS expressions within each template line
foreach ($lines as $line) {
$expressionMatched = static::parseOneTemplateLine($line);
$fakeJs .= implode("; ", $expressionMatched) . "\n";
}
return $fakeJs;
}
/**
* Match JS expressions in a template line
*
* @param string $line
* @return string[]
*/
protected static function parseOneTemplateLine($line)
{
$line = trim($line);
if (!$line) {
return [];
}
$regex = '#\{\{(.*?)\}\}#';
preg_match_all($regex, $line, $matches);
$matched = array_map(function ($v) {
return trim($v, '\'"{}');
}, $matches[1]);
return $matched;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Translation;
use SimpleXMLElement;
/**
* Class to get gettext strings from xliff format.
*/
class Xliff extends Extractor implements ExtractorInterface
{
public static $options = [
'unitid_as_id' => false
];
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$options += static::$options;
$xml = new SimpleXMLElement($string, null, false);
foreach ($xml->file as $file) {
if (isset($file->notes)) {
foreach ($file->notes->note as $note) {
$translations->setHeader($note['id'], (string) $note);
}
}
foreach ($file->unit as $unit) {
foreach ($unit->segment as $segment) {
$targets = [];
foreach ($segment->target as $target) {
$targets[] = (string) $target;
}
$translation = $translations->createNewTranslation(null, (string) $segment->source);
if (isset($unit['id'])) {
$unitId = (string) $unit['id'];
$translation->addComment("XLIFF_UNIT_ID: $unitId");
if ($options['unitid_as_id']) {
$translation->setId($unitId);
}
}
$translation->setTranslation(array_shift($targets));
$translation->setPluralTranslations($targets);
if (isset($unit->notes)) {
foreach ($unit->notes->note as $note) {
switch ($note['category']) {
case 'context':
$translation = $translation->getClone((string) $note);
break;
case 'extracted-comment':
$translation->addExtractedComment((string) $note);
break;
case 'flag':
$translation->addFlag((string) $note);
break;
case 'reference':
$ref = explode(':', (string) $note, 2);
$translation->addReference($ref[0], isset($ref[1]) ? $ref[1] : null);
break;
default:
$translation->addComment((string) $note);
break;
}
}
}
$translations[] = $translation;
}
}
}
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Utils\MultidimensionalArrayTrait;
use Symfony\Component\Yaml\Yaml as YamlParser;
/**
* Class to get gettext strings from yaml.
*/
class Yaml extends Extractor implements ExtractorInterface
{
use MultidimensionalArrayTrait;
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$messages = YamlParser::parse($string);
if (is_array($messages)) {
static::fromArray($messages, $translations);
}
}
}
@@ -0,0 +1,27 @@
<?php
namespace Gettext\Extractors;
use Gettext\Translations;
use Gettext\Utils\DictionaryTrait;
use Symfony\Component\Yaml\Yaml as YamlParser;
/**
* Class to get gettext strings from yaml.
*/
class YamlDictionary extends Extractor implements ExtractorInterface
{
use DictionaryTrait;
/**
* {@inheritdoc}
*/
public static function fromString($string, Translations $translations, array $options = [])
{
$messages = YamlParser::parse($string);
if (is_array($messages)) {
static::fromArray($messages, $translations);
}
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
use Gettext\Utils\HeadersGeneratorTrait;
use Gettext\Utils\CsvTrait;
/**
* Class to export translations to csv.
*/
class Csv extends Generator implements GeneratorInterface
{
use HeadersGeneratorTrait;
use CsvTrait;
public static $options = [
'includeHeaders' => false,
'delimiter' => ",",
'enclosure' => '"',
'escape_char' => "\\"
];
/**
* {@parentDoc}.
*/
public static function toString(Translations $translations, array $options = [])
{
$options += static::$options;
$handle = fopen('php://memory', 'w');
if ($options['includeHeaders']) {
static::fputcsv($handle, ['', '', static::generateHeaders($translations)], $options);
}
foreach ($translations as $translation) {
if ($translation->isDisabled()) {
continue;
}
$line = [$translation->getContext(), $translation->getOriginal(), $translation->getTranslation()];
if ($translation->hasPluralTranslations(true)) {
$line = array_merge($line, $translation->getPluralTranslations());
}
static::fputcsv($handle, $line, $options);
}
rewind($handle);
$csv = stream_get_contents($handle);
fclose($handle);
return $csv;
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
use Gettext\Utils\DictionaryTrait;
use Gettext\Utils\CsvTrait;
class CsvDictionary extends Generator implements GeneratorInterface
{
use DictionaryTrait;
use CsvTrait;
public static $options = [
'includeHeaders' => false,
'delimiter' => ",",
'enclosure' => '"',
'escape_char' => "\\"
];
/**
* {@parentDoc}.
*/
public static function toString(Translations $translations, array $options = [])
{
$options += static::$options;
$handle = fopen('php://memory', 'w');
foreach (static::toArray($translations, $options['includeHeaders']) as $original => $translation) {
static::fputcsv($handle, [$original, $translation], $options);
}
rewind($handle);
$csv = stream_get_contents($handle);
fclose($handle);
return $csv;
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
abstract class Generator implements GeneratorInterface
{
/**
* {@inheritdoc}
*/
public static function toFile(Translations $translations, $file, array $options = [])
{
$content = static::toString($translations, $options);
if (file_put_contents($file, $content) === false) {
return false;
}
return true;
}
}
@@ -0,0 +1,29 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
interface GeneratorInterface
{
/**
* Saves the translations in a file.
*
* @param Translations $translations
* @param string $file
* @param array $options
*
* @return bool
*/
public static function toFile(Translations $translations, $file, array $options = []);
/**
* Generates a string with the translations ready to save in a file.
*
* @param Translations $translations
* @param array $options
*
* @return string
*/
public static function toString(Translations $translations, array $options = []);
}
+66
View File
@@ -0,0 +1,66 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
class Jed extends Generator implements GeneratorInterface
{
public static $options = [
'json' => 0,
];
/**
* {@parentDoc}.
*/
public static function toString(Translations $translations, array $options = [])
{
$domain = $translations->getDomain() ?: 'messages';
$options += static::$options;
return json_encode([
$domain => [
'' => [
'domain' => $domain,
'lang' => $translations->getLanguage() ?: 'en',
'plural-forms' => $translations->getHeader('Plural-Forms') ?: 'nplurals=2; plural=(n != 1);',
],
] + static::buildMessages($translations),
], $options['json']);
}
/**
* Generates an array with all translations.
*
* @param Translations $translations
*
* @return array
*/
protected static function buildMessages(Translations $translations)
{
$pluralForm = $translations->getPluralForms();
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
$messages = [];
$context_glue = '\u0004';
foreach ($translations as $translation) {
if ($translation->isDisabled()) {
continue;
}
$key = ($translation->hasContext() ? $translation->getContext().$context_glue : '')
.$translation->getOriginal();
if ($translation->hasPluralTranslations(true)) {
$message = $translation->getPluralTranslations($pluralSize);
array_unshift($message, $translation->getTranslation());
} else {
$message = [$translation->getTranslation()];
}
$messages[$key] = $message;
}
return $messages;
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
use Gettext\Utils\MultidimensionalArrayTrait;
class Json extends Generator implements GeneratorInterface
{
use MultidimensionalArrayTrait;
public static $options = [
'json' => 0,
'includeHeaders' => false,
];
/**
* {@inheritdoc}
*/
public static function toString(Translations $translations, array $options = [])
{
$options += static::$options;
return json_encode(static::toArray($translations, $options['includeHeaders'], true), $options['json']);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
use Gettext\Utils\DictionaryTrait;
class JsonDictionary extends Generator implements GeneratorInterface
{
use DictionaryTrait;
public static $options = [
'json' => 0,
'includeHeaders' => false,
];
/**
* {@parentDoc}.
*/
public static function toString(Translations $translations, array $options = [])
{
$options += static::$options;
return json_encode(static::toArray($translations, $options['includeHeaders']), $options['json']);
}
}
+140
View File
@@ -0,0 +1,140 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
use Gettext\Utils\HeadersGeneratorTrait;
class Mo extends Generator implements GeneratorInterface
{
use HeadersGeneratorTrait;
public static $options = [
'includeHeaders' => true,
];
/**
* {@parentDoc}.
*/
public static function toString(Translations $translations, array $options = [])
{
$options += static::$options;
$messages = [];
if ($options['includeHeaders']) {
$messages[''] = static::generateHeaders($translations);
}
foreach ($translations as $translation) {
if (!$translation->hasTranslation() || $translation->isDisabled()) {
continue;
}
if ($translation->hasContext()) {
$originalString = $translation->getContext()."\x04".$translation->getOriginal();
} else {
$originalString = $translation->getOriginal();
}
$messages[$originalString] = $translation;
}
ksort($messages);
$numEntries = count($messages);
$originalsTable = '';
$translationsTable = '';
$originalsIndex = [];
$translationsIndex = [];
$pluralForm = $translations->getPluralForms();
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
foreach ($messages as $originalString => $translation) {
if (is_string($translation)) {
// Headers
$translationString = $translation;
} else {
/* @var $translation \Gettext\Translation */
if ($translation->hasPlural() && $translation->hasPluralTranslations(true)) {
$originalString .= "\x00".$translation->getPlural();
$translationString = $translation->getTranslation();
$translationString .= "\x00".implode("\x00", $translation->getPluralTranslations($pluralSize));
} else {
$translationString = $translation->getTranslation();
}
}
$originalsIndex[] = [
'relativeOffset' => strlen($originalsTable),
'length' => strlen($originalString)
];
$originalsTable .= $originalString."\x00";
$translationsIndex[] = [
'relativeOffset' => strlen($translationsTable),
'length' => strlen($translationString)
];
$translationsTable .= $translationString."\x00";
}
// Offset of table with the original strings index: right after the header (which is 7 words)
$originalsIndexOffset = 7 * 4;
// Size of table with the original strings index
$originalsIndexSize = $numEntries * (4 + 4);
// Offset of table with the translation strings index: right after the original strings index table
$translationsIndexOffset = $originalsIndexOffset + $originalsIndexSize;
// Size of table with the translation strings index
$translationsIndexSize = $numEntries * (4 + 4);
// Hashing table starts after the header and after the index table
$originalsStringsOffset = $translationsIndexOffset + $translationsIndexSize;
// Translations start after the keys
$translationsStringsOffset = $originalsStringsOffset + strlen($originalsTable);
// Let's generate the .mo file binary data
$mo = '';
// Magic number
$mo .= pack('L', 0x950412de);
// File format revision
$mo .= pack('L', 0);
// Number of strings
$mo .= pack('L', $numEntries);
// Offset of table with original strings
$mo .= pack('L', $originalsIndexOffset);
// Offset of table with translation strings
$mo .= pack('L', $translationsIndexOffset);
// Size of hashing table: we don't use it.
$mo .= pack('L', 0);
// Offset of hashing table: it would start right after the translations index table
$mo .= pack('L', $translationsIndexOffset + $translationsIndexSize);
// Write the lengths & offsets of the original strings
foreach ($originalsIndex as $info) {
$mo .= pack('L', $info['length']);
$mo .= pack('L', $originalsStringsOffset + $info['relativeOffset']);
}
// Write the lengths & offsets of the translated strings
foreach ($translationsIndex as $info) {
$mo .= pack('L', $info['length']);
$mo .= pack('L', $translationsStringsOffset + $info['relativeOffset']);
}
// Write original strings
$mo .= $originalsTable;
// Write translation strings
$mo .= $translationsTable;
return $mo;
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
use Gettext\Utils\MultidimensionalArrayTrait;
class PhpArray extends Generator implements GeneratorInterface
{
use MultidimensionalArrayTrait;
public static $options = [
'includeHeaders' => true,
];
/**
* {@inheritdoc}
*/
public static function toString(Translations $translations, array $options = [])
{
$array = static::generate($translations, $options);
return '<?php return '.var_export($array, true).';';
}
/**
* Generates an array with the translations.
*
* @param Translations $translations
* @param array $options
*
* @return array
*/
public static function generate(Translations $translations, array $options = [])
{
$options += static::$options;
return static::toArray($translations, $options['includeHeaders'], true);
}
}
+145
View File
@@ -0,0 +1,145 @@
<?php
namespace Gettext\Generators;
use Gettext\Translations;
class Po extends Generator implements GeneratorInterface
{
public static $options = [
'noLocation' => false,
];
/**
* {@parentDoc}.
*/
public static function toString(Translations $translations, array $options = [])
{
$options += static::$options;
$pluralForm = $translations->getPluralForms();
$pluralSize = is_array($pluralForm) ? ($pluralForm[0] - 1) : null;
$lines = ['msgid ""', 'msgstr ""'];
foreach ($translations->getHeaders() as $name => $value) {
$lines[] = sprintf('"%s: %s\\n"', $name, $value);
}
$lines[] = '';
//Translations
foreach ($translations as $translation) {
if ($translation->hasComments()) {
foreach ($translation->getComments() as $comment) {
$lines[] = '# '.$comment;
}
}
if ($translation->hasExtractedComments()) {
foreach ($translation->getExtractedComments() as $comment) {
$lines[] = '#. '.$comment;
}
}
if (!$options['noLocation'] && $translation->hasReferences()) {
foreach ($translation->getReferences() as $reference) {
$lines[] = '#: '.$reference[0].(!is_null($reference[1]) ? ':'.$reference[1] : null);
}
}
if ($translation->hasFlags()) {
$lines[] = '#, '.implode(',', $translation->getFlags());
}
$prefix = $translation->isDisabled() ? '#~ ' : '';
if ($translation->hasContext()) {
$lines[] = $prefix.'msgctxt '.static::convertString($translation->getContext());
}
static::addLines($lines, $prefix.'msgid', $translation->getOriginal());
if ($translation->hasPlural()) {
static::addLines($lines, $prefix.'msgid_plural', $translation->getPlural());
static::addLines($lines, $prefix.'msgstr[0]', $translation->getTranslation());
foreach ($translation->getPluralTranslations($pluralSize) as $k => $v) {
static::addLines($lines, $prefix.'msgstr['.($k + 1).']', $v);
}
} else {
static::addLines($lines, $prefix.'msgstr', $translation->getTranslation());
}
$lines[] = '';
}
return implode("\n", $lines);
}
/**
* Escapes and adds double quotes to a string.
*
* @param string $string
*
* @return string
*/
protected static function multilineQuote($string)
{
$lines = explode("\n", $string);
$last = count($lines) - 1;
foreach ($lines as $k => $line) {
if ($k === $last) {
$lines[$k] = static::convertString($line);
} else {
$lines[$k] = static::convertString($line."\n");
}
}
return $lines;
}
/**
* Add one or more lines depending whether the string is multiline or not.
*
* @param array &$lines
* @param string $name
* @param string $value
*/
protected static function addLines(array &$lines, $name, $value)
{
$newLines = static::multilineQuote($value);
if (count($newLines) === 1) {
$lines[] = $name.' '.$newLines[0];
} else {
$lines[] = $name.' ""';
foreach ($newLines as $line) {
$lines[] = $line;
}
}
}
/**
* Convert a string to its PO representation.
*
* @param string $value
*
* @return string
*/
public static function convertString($value)
{
return '"'.strtr(
$value,
[
"\x00" => '',
'\\' => '\\\\',
"\t" => '\t',
"\r" => '\r',
"\n" => '\n',
'"' => '\\"',
]
).'"';
}
}
+122
View File
@@ -0,0 +1,122 @@
<?php
namespace Gettext\Generators;
use Gettext\Translation;
use Gettext\Translations;
use DOMDocument;
class Xliff extends Generator implements GeneratorInterface
{
const UNIT_ID_REGEXP = '/^XLIFF_UNIT_ID: (.*)$/';
/**
* {@inheritdoc}
*/
public static function toString(Translations $translations, array $options = [])
{
$dom = new DOMDocument('1.0', 'utf-8');
$dom->formatOutput = true;
$xliff = $dom->appendChild($dom->createElement('xliff'));
$xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0');
$xliff->setAttribute('version', '2.0');
$xliff->setAttribute('srcLang', $translations->getLanguage());
$xliff->setAttribute('trgLang', $translations->getLanguage());
$file = $xliff->appendChild($dom->createElement('file'));
$file->setAttribute('id', $translations->getDomain().'.'.$translations->getLanguage());
//Save headers as notes
$notes = $dom->createElement('notes');
foreach ($translations->getHeaders() as $name => $value) {
$notes->appendChild(static::createTextNode($dom, 'note', $value))->setAttribute('id', $name);
}
if ($notes->hasChildNodes()) {
$file->appendChild($notes);
}
foreach ($translations as $translation) {
//Find an XLIFF unit ID, if one is available; otherwise generate
$unitId = static::getUnitID($translation)?:md5($translation->getContext().$translation->getOriginal());
$unit = $dom->createElement('unit');
$unit->setAttribute('id', $unitId);
//Save comments as notes
$notes = $dom->createElement('notes');
$notes->appendChild(static::createTextNode($dom, 'note', $translation->getContext()))
->setAttribute('category', 'context');
foreach ($translation->getComments() as $comment) {
//Skip XLIFF unit ID comments.
if (preg_match(static::UNIT_ID_REGEXP, $comment)) {
continue;
}
$notes->appendChild(static::createTextNode($dom, 'note', $comment))
->setAttribute('category', 'comment');
}
foreach ($translation->getExtractedComments() as $comment) {
$notes->appendChild(static::createTextNode($dom, 'note', $comment))
->setAttribute('category', 'extracted-comment');
}
foreach ($translation->getFlags() as $flag) {
$notes->appendChild(static::createTextNode($dom, 'note', $flag))
->setAttribute('category', 'flag');
}
foreach ($translation->getReferences() as $reference) {
$notes->appendChild(static::createTextNode($dom, 'note', $reference[0].':'.$reference[1]))
->setAttribute('category', 'reference');
}
$unit->appendChild($notes);
$segment = $unit->appendChild($dom->createElement('segment'));
$segment->appendChild(static::createTextNode($dom, 'source', $translation->getOriginal()));
$segment->appendChild(static::createTextNode($dom, 'target', $translation->getTranslation()));
foreach ($translation->getPluralTranslations() as $plural) {
if ($plural !== '') {
$segment->appendChild(static::createTextNode($dom, 'target', $plural));
}
}
$file->appendChild($unit);
}
return $dom->saveXML();
}
protected static function createTextNode(DOMDocument $dom, $name, $string)
{
$node = $dom->createElement($name);
$text = (preg_match('/[&<>]/', $string) === 1)
? $dom->createCDATASection($string)
: $dom->createTextNode($string);
$node->appendChild($text);
return $node;
}
/**
* Gets the translation's unit ID, if one is available.
*
* @param Translation $translation
*
* @return string|null
*/
public static function getUnitID(Translation $translation)
{
foreach ($translation->getComments() as $comment) {
if (preg_match(static::UNIT_ID_REGEXP, $comment, $matches)) {
return $matches[1];
}
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More