]*>(.*)<\/a>/iU', '', $html);
$html = preg_replace("/<\/?h1[^>]*\>/i", "", $html);
$html = preg_replace("/<\/?strong[^>]*\>/i", "", $html);
$html = preg_replace("/<\/?span[^>]*\>/i", "", $html);
//$html = str_replace(' ', '', $html);
$html = str_replace(' ', ' ', $html);
$html = str_replace('\t', ' ', $html);
$html = str_replace(' ', ' ', $html);
$html = preg_replace("/http:\/\/g(\d+)\.a\./i", "https://ae$1.", $html);
$pattern = "/<[^\/>]*>([\s]?)*<\/[^>]*>/";
$html = preg_replace($pattern, '', $html);
$html = str_replace(array('
term_relationships} tr INNER JOIN {$wpdb->term_taxonomy} tt " .
"ON (tt.term_taxonomy_id=tr.term_taxonomy_id) " .
"INNER JOIN {$wpdb->prefix}woocommerce_attribute_taxonomies wat " .
"ON(CONCAT('pa_',wat.attribute_name)=tt.taxonomy) " .
"WHERE tr.object_id = %s AND tt.taxonomy NOT IN (". implode(',', $escAttrs) .")";
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query($wpdb->prepare($sql, $product_id));
}
}
public function set_woocommerce_attributes($post_id, $attributes = array()) {
global $wpdb;
global $woocommerce;
// convert Amazon attributes into woocommerce attributes
$_product_attributes = array();
$position = 0;
foreach ($attributes as $attr) {
$key = $attr['name'];
$value = $attr['value'];
if (!is_object($value)) {
// change dimension name as woocommerce attribute name
$attribute_name = $this->cleanTaxonomyName(strtolower($key));
// Clean
if (is_array($value)) {
foreach($value as $k=>$v){
$value[$k] = $this->cleanValue($v);
}
}else{
$value = $this->cleanValue($value);
}
// if is empty attribute don't import
if (empty($value)){
continue;
}
$_product_attributes[$attribute_name] = array(
'name' => $attribute_name,
'value' => is_array($value)?implode(", ", $value):$value,
'position' => $position++,
'is_visible' => 1,
'is_variation' => 0,
'is_taxonomy' => 1
);
$this->add_attribute($post_id, $key, $value);
}
}
// update product attribute
update_post_meta($post_id, '_product_attributes', $_product_attributes);
$this->attrclean_clean_all('array'); // delete duplicate attributes
// refresh attribute cache
//$dmtransient_name = 'wc_attribute_taxonomies';
//$dmattribute_taxonomies = $wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "woocommerce_attribute_taxonomies");
//set_transient($dmtransient_name, $dmattribute_taxonomies);
flush_rewrite_rules();
a2wl_delete_transient('wc_attribute_taxonomies');
}
public function add_attribute($post_id, $key, $value): void
{
global $wpdb;
// avoid object to be inserted in terms
if (is_object($value)) {
return;
}
// Empty attribute values are expected for sold-out/disabled products (the attribute
// itself is still registered in advance). Not a bug; keep as a case to verify with
// a dedicated test in the future.
if (empty($value) || (is_array($value) && count(array_filter($value)) === 0)) {
a2wl_info_log(sprintf(
'[add_attribute] Empty value detected for product_id=%d, key=%s',
$post_id,
$key
));
}
$taxonomy = $this->cleanTaxonomyName($key);
$attribute_label = $key;
//attribute name should be the same as taxonomy just without pa_
$attribute_name = str_replace('pa_', '', $taxonomy);
$attribute_type = 'select';
$attribute_taxonomies = $wpdb->get_var(
"SELECT * FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_name = '" . esc_sql($attribute_name) . "'"
);
if ($attribute_taxonomies) {
// Update existing global attribute
// Set type to 'select' since this attribute is used for variations
$wpdb->update(
$wpdb->prefix . 'woocommerce_attribute_taxonomies',
[
'attribute_type' => $attribute_type
],
['attribute_name' => $attribute_name]
);
} else {
$wpdb->insert(
$wpdb->prefix . 'woocommerce_attribute_taxonomies', [
'attribute_label' => $attribute_label,
'attribute_name' => $attribute_name,
'attribute_type' => $attribute_type,
'attribute_orderby' => 'name'
]
);
}
// add attribute values if not exist
$values = is_array($value) ? $value : [$value];
// check taxonomy
if (!taxonomy_exists($taxonomy)) {
// add attribute value
foreach ($values as $attribute_value) {
$attribute_value = (string) $attribute_value;
if (is_string($attribute_value)) {
// add term
$name = $this->cleanValue($attribute_value);
$slug = sanitize_title($name);
if (!term_exists($name)) {
$term_taxonomy_id = $this->createTermAndLinkTaxonomy($name, $taxonomy, $slug);
} else {
$term_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT term_id FROM {$wpdb->terms} WHERE name = %s",
$name
)
);
$existing_tt_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE term_id = %d AND taxonomy = %s",
$term_id,
$taxonomy
)
);
if ($existing_tt_id) {
//term is connected to the taxonomy, do nothing
$term_taxonomy_id = $existing_tt_id;
} else {
//need create a new token and connect it to the taxonomy
$term_taxonomy_id = $this->createTermAndLinkTaxonomy($name, $taxonomy, $slug);
}
}
}
}
}
if (!empty($values)) {
$attribute_slugs = array();
$terms = $this->load_terms($taxonomy);
foreach ($terms as $term) {
$attribute_slugs[] = strval($term->slug);
}
// Add terms
foreach ($values as $attribute_value) {
$attribute_value = $this->cleanValue((string) $attribute_value);
$attribute_slug = sanitize_title(htmlspecialchars($attribute_value, ENT_NOQUOTES));
if (!in_array(strval($attribute_slug), $attribute_slugs, true)) {
wp_insert_term($attribute_value, $taxonomy, array('slug'=>$attribute_slug));
}
}
}
// wp_term_relationships (object_id to term_taxonomy_id)
if (!empty($values)) {
foreach ($values as $term) {
if (!is_array($term) && !is_object($term)) {
$term = sanitize_title(htmlspecialchars($term, ENT_NOQUOTES));
$term_taxonomy_id = $wpdb->get_var("SELECT tt.term_taxonomy_id FROM {$wpdb->terms} AS t INNER JOIN {$wpdb->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE t.slug = '" . esc_sql($term) . "' AND tt.taxonomy = '" . esc_sql($taxonomy) . "'");
if ($term_taxonomy_id) {
$checkSql = "SELECT * FROM {$wpdb->term_relationships} WHERE object_id = {$post_id} AND term_taxonomy_id = {$term_taxonomy_id}";
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if (!$wpdb->get_var($checkSql)) {
$wpdb->insert(
$wpdb->term_relationships,
array('object_id' => $post_id, 'term_taxonomy_id' => $term_taxonomy_id)
);
}
}
}
}
}
// Update WPML translates
global $woocommerce_wpml;
if (isset($woocommerce_wpml) && property_exists($woocommerce_wpml, 'attributes') && $woocommerce_wpml->attributes) {
$woocommerce_wpml->attributes->set_attribute_config_in_settings( $taxonomy, 1 );
}
}
public function attrclean_clean_all($retType = 'die') {
// :: get duplicates list
$duplicates = $this->attrclean_getDuplicateList();
if (empty($duplicates) || !is_array($duplicates)) {
$ret['status'] = 'valid';
$ret['msg_html'] = 'no duplicate terms found!';
if ($retType == 'die')
die(wp_json_encode($ret));
else
return $ret;
}
// html message
$__duplicates = array();
$__duplicates[] = '0 : name, slug, term_taxonomy_id, taxonomy, count';
foreach ($duplicates as $key => $value) {
$__duplicates[] = $value->name . ' : ' . implode(', ', (array) $value);
}
$ret['status'] = 'valid';
$ret['msg_html'] = implode('
', $__duplicates);
// if ( $retType == 'die' ) die(wp_json_encode($ret));
// else return $ret;
// :: get terms per duplicate
$__removeStat = array();
$__terms = array();
$__terms[] = '0 : term_id, name, slug, term_taxonomy_id, taxonomy, count';
foreach ($duplicates as $key => $value) {
$terms = $this->attrclean_getTermPerDuplicate($value->name, $value->taxonomy);
if (empty($terms) || !is_array($terms) || count($terms) < 2)
continue 1;
$first_term = array_shift($terms);
// html message
foreach ($terms as $k => $v) {
$__terms[] = $key . ' : ' . implode(', ', (array) $v);
}
// :: remove duplicate term
$removeStat = $this->attrclean_removeDuplicate($first_term->term_id, $terms, false);
// html message
$__removeStat[] = '-------------------------------------- ' . $key;
$__removeStat[] = '---- term kept';
$__removeStat[] = 'term_id, term_taxonomy_id';
$__removeStat[] = $first_term->term_id . ', ' . $first_term->term_taxonomy_id;
foreach ($removeStat as $k => $v) {
$__removeStat[] = '---- ' . $k;
if (!empty($v) && is_array($v)) {
foreach ($v as $k2 => $v2) {
$__removeStat[] = implode(', ', (array) $v2);
}
} else if (!is_array($v)) {
$__removeStat[] = (int) $v;
} else {
$__removeStat[] = 'empty!';
}
}
}
$ret['status'] = 'valid';
$ret['msg_html'] = implode('
', $__removeStat);
if ($retType == 'die')
die(wp_json_encode($ret));
else
return $ret;
}
/**
* Attributes clean duplicate
*/
public function attrclean_getDuplicateList() {
global $wpdb;
// $q = "SELECT COUNT(a.term_id) AS nb, a.name, a.slug FROM {$wpdb->terms} AS a WHERE 1=1 GROUP BY a.name HAVING nb > 1;";
$q = "SELECT COUNT(a.term_id) AS nb, a.name, a.slug, b.term_taxonomy_id, b.taxonomy, b.count FROM {$wpdb->terms} AS a " .
"LEFT JOIN {$wpdb->term_taxonomy} AS b ON a.term_id = b.term_id " .
"WHERE b.taxonomy REGEXP '^pa_' GROUP BY a.name, b.taxonomy HAVING nb > 1 " .
"ORDER BY a.name ASC";
$res = $wpdb->get_results(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->prepare($q)
);
if (!$res || !is_array($res))
return false;
$ret = array();
foreach ($res as $key => $value) {
$name = $value->name;
$taxonomy = $value->taxonomy;
$ret["$name@@$taxonomy"] = $value;
}
return $ret;
}
public function attrclean_getTermPerDuplicate($term_name, $taxonomy) {
global $wpdb;
$q = "SELECT a.term_id, a.name, a.slug, b.term_taxonomy_id, b.taxonomy, b.count FROM {$wpdb->terms} AS a " .
"LEFT JOIN {$wpdb->term_taxonomy} AS b ON a.term_id = b.term_id " .
"WHERE a.name=%s AND b.taxonomy=%s ORDER BY a.slug ASC";
$res = $wpdb->get_results(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->prepare($q, $term_name, $taxonomy)
);
if (!$res || !is_array($res))
return false;
$ret = array();
foreach ($res as $key => $value) {
$ret[$value->term_taxonomy_id] = $value;
}
return $ret;
}
public function attrclean_removeDuplicate($first_term, $terms = array(), $debug = false) {
if (empty($terms) || !is_array($terms))
return false;
$term_id = array();
$term_taxonomy_id = array();
foreach ($terms as $k => $v) {
$term_id[] = $v->term_id;
$term_taxonomy_id[] = $v->term_taxonomy_id;
$taxonomy = $v->taxonomy;
}
// var_dump('',$first_term, $term_id, $term_taxonomy_id, $taxonomy,'
');
$ret = array();
$ret['term_relationships'] = $this->attrclean_remove_term_relationships($first_term, $term_taxonomy_id, $debug);
$ret['terms'] = $this->attrclean_remove_terms($term_id, $debug);
$ret['term_taxonomy'] = $this->attrclean_remove_term_taxonomy($term_taxonomy_id, $taxonomy, $debug);
// var_dump('',$ret,'
');
return $ret;
}
/**
* Create a new term and link it to the given taxonomy.
*
* Normally each taxonomy should have its own terms, even if names/slugs match.
* This function uses wp_unique_term_slug() as a safeguard in case another process
* already inserted the same slug for this taxonomy, ensuring uniqueness and avoiding conflicts.
*
* @param string $termName Term name (e.g. "white")
* @param string $taxonomy Taxonomy name (e.g. "pa_color")
* @param string $slug Base slug for the term
* @return int|false term_taxonomy_id or false on failure
*/
private function createTermAndLinkTaxonomy($termName, $taxonomy, $slug) {
global $wpdb;
if (trim($slug) !== '' && trim($termName) !== '') {
$term = (object) [
'taxonomy' => $taxonomy,
'parent' => 0,
'term_id' => 0,
];
$unique_slug = wp_unique_term_slug($slug, $term);
$this->db_custom_insert(
$wpdb->terms,
[
'values' => ['name' => $termName, 'slug' => $unique_slug],
'format' => ['%s', '%s']
],
true
);
$term_id = $wpdb->insert_id;
$this->db_custom_insert(
$wpdb->term_taxonomy,
[
'values' => ['term_id' => $term_id, 'taxonomy' => $taxonomy],
'format' => ['%d', '%s']
],
true
);
return $wpdb->insert_id;
}
return false;
}
private function attrclean_remove_term_relationships($first_term, $term_taxonomy_id, $debug = false) {
global $wpdb;
$idList = (is_array($term_taxonomy_id) && count($term_taxonomy_id) > 0 ?
implode(', ', array_map(array($this, 'prepareForInList'), $term_taxonomy_id)) :
0);
if ($debug) {
$q = "SELECT a.object_id, a.term_taxonomy_id FROM {$wpdb->term_relationships} AS a " .
"WHERE a.term_taxonomy_id IN ({$idList}) ORDER BY a.object_id ASC, a.term_taxonomy_id";
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$res = $wpdb->get_results($q);
if (!$res || !is_array($res))
return false;
$ret = array();
$ret[] = 'object_id, term_taxonomy_id';
foreach ($res as $key => $value) {
$term_taxonomy_id = $value->term_taxonomy_id;
$ret["$term_taxonomy_id"] = $value;
}
return $ret;
}
// execution/ update
$q = "UPDATE {$wpdb->term_relationships} AS a SET a.term_taxonomy_id = %s " .
"WHERE a.term_taxonomy_id IN ({$idList})";
$res = $wpdb->query(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->prepare($q, $first_term)
);
$ret = $res;
return $ret;
}
private function attrclean_remove_terms($term_id, $debug = false) {
global $wpdb;
$idList = (is_array($term_id) && count($term_id) > 0 ? implode(', ', array_map(array($this, 'prepareForInList'), $term_id)) : 0);
if ($debug) {
$q = "SELECT a.term_id, a.name FROM {$wpdb->terms} AS a " .
"WHERE a.term_id IN (%s) ORDER BY a.name ASC";
$res = $wpdb->get_results(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->prepare($q, $idList)
);
if (!$res || !is_array($res))
return false;
$ret = array();
$ret[] = 'term_id, name';
foreach ($res as $key => $value) {
$term_id = $value->term_id;
$ret["$term_id"] = $value;
}
return $ret;
}
// execution/ update
$q = "DELETE FROM a USING {$wpdb->terms} as a WHERE a.term_id IN ({$idList})";
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
return $wpdb->query($q);
}
private function attrclean_remove_term_taxonomy($term_taxonomy_id, $taxonomy, $debug = false) {
global $wpdb;
$idList = (
is_array($term_taxonomy_id) && count($term_taxonomy_id) > 0 ?
implode(', ', array_map(array($this, 'prepareForInList'), $term_taxonomy_id)) :
0
);
if ($debug) {
$q = "SELECT a.term_id, a.taxonomy, a.term_taxonomy_id FROM {$wpdb->term_taxonomy} AS a " .
"WHERE a.term_taxonomy_id IN ({$idList}) AND a.taxonomy = %s ORDER BY a.term_taxonomy_id ASC";
$res = $wpdb->get_results(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->prepare($q, esc_sql($taxonomy))
);
if (!$res || !is_array($res))
return false;
$ret = array();
$ret[] = 'term_id, taxonomy, term_taxonomy_id';
foreach ($res as $key => $value) {
$term_taxonomy_id = $value->term_taxonomy_id;
$ret["$term_taxonomy_id"] = $value;
}
return $ret;
}
// execution/ update
$q = "DELETE FROM a USING {$wpdb->term_taxonomy} as a WHERE a.term_taxonomy_id IN ({$idList}) AND a.taxonomy = %s";
return $wpdb->query(
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->prepare($q, esc_sql($taxonomy))
);
}
public function load_terms(string $taxonomy) {
global $wpdb;
$query = "SELECT DISTINCT t.name, t.slug FROM {$wpdb->terms} AS t " .
"INNER JOIN {$wpdb->term_taxonomy} as tt ON tt.term_id = t.term_id " .
"WHERE tt.taxonomy = %s";
return $wpdb->get_results($wpdb->prepare($query, $taxonomy), OBJECT);
}
public function db_custom_insert(string $table, array $fields, bool $ignore = false, bool $wp_way = false): int
{
global $wpdb;
if ($wp_way && !$ignore) {
$wpdb->insert($table, $fields['values'], $fields['format']);
} else {
$columns = [];
foreach ($fields['values'] as $k => $v) {
$columns[] = $k;
}
$query = "INSERT " . ($ignore ? "IGNORE" : "") .
" INTO $table (" . implode(', ', $columns) . ")" .
" VALUES (" . implode(', ', $fields['format']) . ");";
$valuesForPrepare = array_values($fields['values']);
$wpdb->query($wpdb->prepare($query, $valuesForPrepare));
}
return $wpdb->insert_id;
}
public function prepareForInList($v): string
{
return "'" . $v . "'";
}
public function cleanTaxonomyName(string $value, bool $withPrefix = true, bool $checkSize = true): string
{
// Sanitize taxonomy names. Slug format (no spaces, lowercase) - uses sanitize_title
if ($withPrefix) {
$ret = wc_attribute_taxonomy_name($value); // return 'pa_' . $value
} else {
$ret = wc_sanitize_taxonomy_name($value); // return $value
}
if ($checkSize) {
// limit to 32 characters (database/ table wp_term_taxonomy/ field taxonomy/ is limited to varchar(32) )
if (wp_is_valid_utf8($ret)) {
$limit_max = $withPrefix ? 18 : 15; // utf8: 3 + 29/2
if (function_exists('mb_substr')) {
$ret = mb_substr($ret, 0, $limit_max);
// Ensure generated name doesn't exceed byte length using strlen (because Woocommerce use strlen)
while (strlen($ret) > ($withPrefix ? 32 : 28)) {
$ret = mb_substr($ret, 0, -1, 'UTF-8'); // remove last character
}
}
} else {
$limit_max = $withPrefix ? 32 : 29; // 29 = 32 - strlen('pa_')
$ret = substr($ret, 0, $limit_max);
}
}
// IMPORTANT, if not sure do not need sanitize_title!
return $ret;
}
public function cleanValue(string $value): string
{
// Format Camel Case
//$value = trim( preg_replace('/([A-Z])/', ' $1', $value) );
// Clean / from value
return trim(preg_replace('/(\/)/', '-', $value));
}
public function multi_implode($array, $glue) {
$ret = '';
foreach ($array as $item) {
if (is_array($item)) {
$ret .= $this->multi_implode($item, $glue) . $glue;
} else {
$ret .= $item . $glue;
}
}
$ret = substr($ret, 0, 0 - strlen($glue));
return $ret;
}
}