Блог

  • Типизация слотов в Vue 3: Как исправить ошибку TS7006 в TypeScript

    При работе с Vue 3 и TypeScript разработчики часто сталкиваются с ошибкой TS7006: Parameter implicitly has an 'any' type при использовании scoped-слотов. В этой статье разберём, как правильно типизировать слоты и избежать этой проблемы.

    Почему возникает ошибка TS7006?

    Ошибка появляется, когда TypeScript не может определить тип параметров слота:

    <template #export="slotProps">
      <ExportButton v-bind="slotProps" />
    </template>

    TypeScript выдаёт: TS7006: Parameter 'slotProps' implicitly has an 'any' type

    Решение: Явная типизация слотов

    Способ 1: Указание типа для объекта

    <template #export="props: ExportSlotProps">
      <ExportButton v-bind="props" />
    </template>

    Способ 2: Деструктуризация с типами (рекомендуется)

    <template #export="{
      getVisibleColumns,
      prepareFilterParams,
      page,
      totalItems
    }: ExportSlotProps">
      <ExportButton
        :getVisibleColumns="getVisibleColumns"
        :prepareFilterParams="prepareFilterParams"
        :page="page"
        :total-items="totalItems"
      />
    </template>

    Настройка типов в дочернем компоненте

    Для автоматического определения типов добавьте в компонент:

    <script setup lang="ts">
    import type { VNode } from 'vue'
    
    interface ExportSlotProps {
      getVisibleColumns: () => string[]
      prepareFilterParams: () => Record<string, unknown>
      page: string
      totalItems: number
    }
    
    defineSlots<{
      export?: (props: ExportSlotProps) => VNode[]
    }>()
    </script>

    Заключение

    Для устранения TS7006:

    • Используйте явную типизацию в шаблоне
    • Применяйте defineSlots() в дочерних компонентах
    • Выносите общие типы в отдельные файлы
  • Vue 3 Slots передача параметров

    Slots (слоты) в Vue 3 предоставляют мощный механизм для создания гибких и переиспользуемых компонентов. В этой статье мы разберём все способы передачи параметров в слоты с примерами из реальной практики.

    Основные типы слотов в Vue 3

    • Слоты по умолчанию — базовый способ передачи контента
    • Именованные слоты — для точного позиционирования контента
    • Scoped slots — с передачей параметров из дочернего компонента

    1. Scoped Slots (Основной способ передачи параметров)

    <!-- Дочерний компонент -->
    <template>
      <div>
        <slot :item="item" :index="index"></slot>
      </div>
    </template>
    
    <!-- Родительский компонент -->
    <ChildComponent>
      <template v-slot:default="slotProps">
        {{ slotProps.item }} - {{ slotProps.index }}
      </template>
    </ChildComponent>

    2. Именованные scoped slots

    <!-- Дочерний компонент -->
    <template>
      <div>
        <slot name="header" :title="title"></slot>
        <slot name="content" :data="contentData"></slot>
      </div>
    </template>
    
    <!-- Родительский компонент -->
    <DataContainer>
      <template #header="{ title }">
        <h2>{{ title }}</h2>
      </template>
      
      <template #content="{ data }">
        <p>{{ data.description }}</p>
      </template>
    </DataContainer>

    3. Динамические параметры слотов

    <!-- Дочерний компонент -->
    <script setup>
    const slotProps = computed(() => ({
      user: currentUser.value,
      timestamp: new Date()
    }))
    </script>
    
    <template>
      <slot v-bind="slotProps"></slot>
    </template>

    4. Деструктуризация параметров

    <template #item="{ id, name }">
      <div>{{ id }}: {{ name }}</div>
    </template>

    Практические примеры

    Пример 1: Гибкий список

    <SmartList :items="users">
      <template #item="{ user }">
        <UserCard :user="user" />
      </template>
    </SmartList>

    Пример 2: Модальное окно с параметрами

    <ModalDialog>
      <template #header="{ close }">
        <button @click="close">×</button>
      </template>
      
      <template #default="{ data }">
        {{ data.message }}
      </template>
    </ModalDialog>

    Лучшие практики

    • Используйте осмысленные имена для параметров слотов
    • Для сложных компонентов документируйте структуру слотов
    • Избегайте глубокой вложенности scoped slots
    • Используйте TypeScript для типизации параметров

    Заключение

    Scoped slots в Vue 3 предоставляют мощный инструмент для создания гибких компонентов. Освоив передачу параметров в слоты, вы сможете создавать по-настоящему переиспользуемые UI-компоненты.

    Для более сложных сценариев рассмотрите использование Composition API вместе со слотами.

    Vue 3 Composition API + Slots: Сложные сценарии использования

    В сочетании с Composition API слоты Vue 3 раскрывают свою настоящую мощь. Рассмотрим продвинутые паттерны для сложных UI-компонентов.

    1. Динамические слоты с реактивными параметрами

    <!-- DynamicTable.vue -->
    <script setup>
    import { ref, computed } from 'vue'
    
    const props = defineProps(['data'])
    const sortDirection = ref('asc')
    
    const sortedData = computed(() => {
      return [...props.data].sort((a, b) => {
        return sortDirection.value === 'asc' 
          ? a.value - b.value 
          : b.value - a.value
      })
    })
    </script>
    
    <template>
      <table>
        <slot name="header" :toggleSort="() => sortDirection = sortDirection === 'asc' ? 'desc' : 'asc'"></slot>
        <tr v-for="(item, index) in sortedData" :key="index">
          <slot :item="item" :index="index"/>
        </tr>
      </table>
    </template>

    Применение: Таблицы с сортировкой, где логика инкапсулирована в компоненте, а рендеринг контролируется через слоты.

    2. Состояние модальных окон

    <!-- useModal.js -->
    import { ref } from 'vue'
    
    export function useModal() {
      const isOpen = ref(false)
      
      const open = () => isOpen.value = true
      const close = () => isOpen.value = false
      
      return { isOpen, open, close }
    }
    
    <!-- ModalComponent.vue -->
    <script setup>
    import { useModal } from './useModal'
    const modal = useModal()
    </script>
    
    <template>
      <slot :open="modal.open" :close="modal.close" :isOpen="modal.isOpen"/>
    </template>

    Использование:

    <ModalComponent v-slot="{ open, close, isOpen }">
      <button @click="open">Открыть</button>
      
      <div v-if="isOpen" class="modal">
        <slot name="content"/>
        <button @click="close">Закрыть</button>
      </div>
    </ModalComponent>

    3. Композиционные слоты для форм

    <!-- useFormField.js -->
    import { ref, computed } from 'vue'
    
    export function useFormField(initialValue) {
      const value = ref(initialValue)
      const isValid = computed(() => value.value.length > 0)
      
      return { value, isValid }
    }
    
    <!-- FormField.vue -->
    <script setup>
    import { useFormField } from './useFormField'
    
    const props = defineProps(['initialValue'])
    const field = useFormField(props.initialValue)
    </script>
    
    <template>
      <div class="form-field" :class="{ invalid: !field.isValid }">
        <slot :value="field.value" :isValid="field.isValid"/>
      </div>
    </template>

    Использование с кастомным input:

    <FormField initialValue="" v-slot="{ value, isValid }">
      <input 
        v-model="value"
        :class="{ error: !isValid }"
        placeholder="Введите текст"
      >
      <span v-if="!isValid">Поле обязательно</span>
    </FormField>

    4. Сложные компоненты данных

    <!-- DataFetcher.vue -->
    <script setup>
    import { ref, onMounted } from 'vue'
    
    const props = defineProps(['url'])
    const data = ref(null)
    const error = ref(null)
    const isLoading = ref(false)
    
    onMounted(async () => {
      isLoading.value = true
      try {
        const response = await fetch(props.url)
        data.value = await response.json()
      } catch (err) {
        error.value = err
      } finally {
        isLoading.value = false
      }
    })
    </script>
    
    <template>
      <slot 
        :data="data" 
        :error="error" 
        :isLoading="isLoading"
        :reload="onMounted"
      />
    </template>

    Использование:

    <DataFetcher url="/api/users" v-slot="{ data, isLoading }">
      <div v-if="isLoading">Загрузка...</div>
      <UserList v-else :users="data" />
    </DataFetcher>

    5. Продвинутый пример: Компонент вкладок

    <!-- TabsContainer.vue -->
    <script setup>
    import { ref } from 'vue'
    
    const activeTab = ref(0)
    const tabs = ref([])
    
    const registerTab = (title) => {
      const id = tabs.value.length
      tabs.value.push({ id, title })
      return id
    }
    
    const setActiveTab = (id) => {
      activeTab.value = id
    }
    </script>
    
    <template>
      <div class="tabs-container">
        <div class="tabs-header">
          <button 
            v-for="tab in tabs" 
            @click="setActiveTab(tab.id)"
            :class="{ active: activeTab === tab.id }"
          >
            {{ tab.title }}
          </button>
        </div>
        
        <div class="tabs-content">
          <slot :activeTab="activeTab"/>
        </div>
      </div>
    </template>

    Использование:

    <TabsContainer v-slot="{ activeTab }">
      <template #default>
        <Tab :register="registerTab" title="Профиль">
          <div v-if="activeTab === 0">...</div>
        </Tab>
        
        <Tab :register="registerTab" title="Настройки">
          <div v-if="activeTab === 1">...</div>
        </Tab>
      </template>
    </TabsContainer>

    Заключение

    Сочетание Composition API и слотов позволяет создавать:

    • Полностью инкапсулированную логику компонентов
    • Максимально гибкие API для переиспользуемых компонентов
    • Сложные stateful-компоненты с простым интерфейсом
    • Легко тестируемые решения (бизнес-логика отделена от рендеринга)

  • Как получить ID сущности Doctrine без загрузки всей сущности в Symfony

    При работе с Doctrine ORM в Symfony часто возникает необходимость получить только идентификатор сущности, не загружая все её данные из базы данных. Это особенно важно для оптимизации производительности при работе с большими объемами данных.

    Почему это важно?

    • Уменьшение количества запросов к БД
    • Снижение потребления памяти
    • Повышение скорости выполнения операций
    • Оптимизация работы с ассоциациями

    Способы получения ID без загрузки сущности

    1. Использование getReference()

    // Получаем ссылку на сущность без её загрузки
    $entityReference = $entityManager->getReference(Product::class, $id);
    $entityId = $entityReference->getId();

    2. DQL с выборкой только ID

    $query = $entityManager->createQuery(
        'SELECT p.id FROM App\Entity\Product p WHERE p.price > :price'
    )->setParameter('price', 100);
    
    $ids = $query->getResult(); // Возвращает массив ID

    3. QueryBuilder с выборкой ID

    $ids = $entityManager->getRepository(Product::class)
        ->createQueryBuilder('p')
        ->select('p.id')
        ->where('p.stock > 0')
        ->getQuery()
        ->getResult();

    4. Работа с ассоциациями

    // Получаем ID связанной сущности без её полной загрузки
    $categoryId = $product->getCategory()->getId();
    // Doctrine использует прокси-объекты для ленивой загрузки

    Практический пример

    Рассмотрим реальный сценарий — нам нужно получить список ID товаров, которые нужно обновить:

    public function getProductIdsToUpdate(EntityManagerInterface $em): array
    {
        return $em->getRepository(Product::class)
            ->createQueryBuilder('p')
            ->select('p.id')
            ->where('p.updatedAt < :date')
            ->setParameter('date', new \DateTime('-1 week'))
            ->getQuery()
            ->getSingleColumnResult();
    }

    Заключение

    Использование этих методов позволяет значительно оптимизировать работу с базой данных в Symfony-приложениях. Выбор конкретного способа зависит от вашего сценария использования, но в большинстве случаев QueryBuilder с явным указанием select(‘id’) будет наиболее оптимальным решением.

    Помните, что преждевременная оптимизация может быть вредна — используйте эти подходы там, где действительно есть проблемы с производительностью.

  • Как очистить таблицу MySQL с внешними ключами — 4 способа

    При администрировании баз данных MySQL разработчики часто сталкиваются с ошибкой «Cannot truncate a table referenced in a foreign key constraint». Эта статья подробно объяснит причины ошибки и предложит несколько проверенных способов решения с детальным разбором каждого метода.

    Почему MySQL запрещает TRUNCATE для таблиц с внешними ключами?

    Ошибка возникает из-за фундаментального различия между операторами DELETE и TRUNCATE:

    • TRUNCATE — операция DDL (Data Definition Language), которая:
      • Удаляет и воссоздаёт таблицу
      • Не регистрирует отдельные удаления в логе транзакций
      • Сбрасывает автоинкремент
      • Не проверяет ограничения FOREIGN KEY
    • DELETE — операция DML (Data Manipulation Language), которая:
      • Удаляет строки по одной
      • Проверяет все ограничения
      • Может быть частью транзакции

    Детальный разбор методов решения

    1. Временное отключение проверки внешних ключей

    SET FOREIGN_KEY_CHECKS = 0;
    TRUNCATE TABLE products;
    SET FOREIGN_KEY_CHECKS = 1;

    Что происходит:

    • FOREIGN_KEY_CHECKS = 0 отключает проверку ограничений на уровне сессии
    • TRUNCATE выполняется без проверки связанных таблиц
    • FOREIGN_KEY_CHECKS = 1 восстанавливает проверку

    Когда использовать: В разработке и тестовых средах, когда вам нужно быстро очистить таблицу.

    Внимание: Этот метод может нарушить целостность данных, если в связанных таблицах останутся ссылки на удалённые записи.

    2. Использование DELETE с последующим сбросом автоинкремента

    DELETE FROM products;
    ALTER TABLE products AUTO_INCREMENT = 1;

    Что происходит:

    • DELETE удаляет все строки, проверяя ограничения
    • ALTER TABLE сбрасывает счётчик автоинкремента
    • Операция записывается в лог транзакций

    Когда использовать: В production-среде, когда важна целостность данных.

    Недостатки: Для больших таблиц выполняется медленнее, чем TRUNCATE.

    3. Полное удаление и воссоздание таблицы

    DROP TABLE products;
    CREATE TABLE products (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(255) NOT NULL,
        ...
    );

    Что происходит:

    • DROP TABLE полностью удаляет таблицу и все её зависимости
    • CREATE TABLE создаёт новую чистую таблицу
    • Все индексы и ограничения нужно определить заново

    Когда использовать: При изменении структуры таблицы или в тестовых средах.

    4. Удаление и восстановление ограничений

    -- 1. Узнаём имя ограничения
    SELECT TABLE_NAME, CONSTRAINT_NAME 
    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
    WHERE CONSTRAINT_TYPE = 'FOREIGN KEY' 
    AND TABLE_NAME = 'order_items';
    
    -- 2. Удаляем ограничение
    ALTER TABLE order_items DROP FOREIGN KEY fk_order_items_products;
    
    -- 3. Очищаем таблицу
    TRUNCATE TABLE products;
    
    -- 4. Восстанавливаем ограничение
    ALTER TABLE order_items ADD CONSTRAINT fk_order_items_products 
    FOREIGN KEY (product_id) REFERENCES products(id) 
    ON DELETE RESTRICT ON UPDATE CASCADE;

    Что происходит:

    • Сначала находим точное имя ограничения
    • Временно удаляем FOREIGN KEY
    • Выполняем TRUNCATE
    • Восстанавливаем ограничение с теми же параметрами

    Когда использовать: Когда нужно сохранить все другие настройки таблицы.

    Рекомендации для разных сценариев

    СценарийРекомендуемый методПримечания
    Разработка/тестированиеОтключение FOREIGN_KEY_CHECKSБыстро, но требует осторожности
    Production-средаDELETE + ALTER TABLEБезопасно, но медленно для больших таблиц
    Изменение структурыDROP + CREATE TABLEПолная пересоздание структуры
    Миграции данныхУдаление/восстановление ограниченийТочный контроль над процессом

    Дополнительные советы

    • Всегда делайте бэкап перед массовыми операциями
    • Используйте транзакции для безопасности:
      START TRANSACTION;
      SET FOREIGN_KEY_CHECKS = 0;
      TRUNCATE TABLE products;
      SET FOREIGN_KEY_CHECKS = 1;
      COMMIT;

    • Для Doctrine в Symfony:
      $this->addSql('SET FOREIGN_KEY_CHECKS = 0');
      $this->addSql('TRUNCATE TABLE products');
      $this->addSql('SET FOREIGN_KEY_CHECKS = 1');

  • Полный гид по алгоритмам сортировки: от базовых до специализированных методов

    В мире компьютерных наук существует более 50 различных алгоритмов сортировки, каждый из которых имеет уникальные характеристики и области применения. Давайте систематизируем эти методы и разберём их ключевые особенности.

    1. Основные категории алгоритмов сортировки

    1.1. Сравнительные сортировки

    Эти алгоритмы основаны на попарном сравнении элементов:

    • Обменные сортировки (Bubble Sort, Comb Sort, Quick Sort) — меняют местами элементы в процессе работы
    • Сортировки вставками (Insertion Sort, Shell Sort) — последовательно вставляют элементы в отсортированную часть
    • Сортировки выбором (Selection Sort, Heap Sort) — последовательно выбирают экстремальные элементы
    • Гибридные методы (Timsort, Introsort) — комбинируют разные подходы

    1.2. Целочисленные сортировки

    Специализированные методы для чисел и данных с известными свойствами:

    • Быстрые сортировки (Counting Sort, Radix Sort, Bucket Sort) — используют свойства числовых ключей
    • Битовые сортировки (Bitonic Sort, American Flag Sort) — работают с битовыми представлениями
    • Гибридные числовые (Flash Sort, Postman Sort) — сочетают разные подходы

    2. Подробный разбор ключевых алгоритмов

    2.1. Эффективные сравнительные сортировки

    QuickSort (1960, Тони Хоар):
    Использует стратегию «разделяй и властвуй» с выбором опорного элемента. В среднем случае работает за O(n log n), но может деградировать до O(n²) при неудачном выборе pivot. Современные реализации используют:

    • Медиану трёх для выбора pivot
    • Переключение на Heap Sort при глубокой рекурсии
    • Оптимизацию для маленьких подмассивов

    Timsort (2002, Тим Петерс):
    Гибридный алгоритм, сочетающий Merge Sort и Insertion Sort. Особенности:

    • Выделяет уже отсортированные подпоследовательности (runs)
    • Оптимален для реальных данных с частичной упорядоченностью
    • Используется в Python и Java

    2.2. Специализированные числовые сортировки

    Radix Sort:
    Поразрядная сортировка, которая обрабатывает цифры чисел от младших к старшим. Варианты:

    • LSD (Least Significant Digit) — от младших разрядов к старшим
    • MSD (Most Significant Digit) — от старших разрядов к младшим
    • Гибридные версии с переключением на другой алгоритм

    Counting Sort:
    Эффективен когда диапазон значений (k) много меньше количества элементов (n). Применяется как подпрограмма для Radix Sort.

    3. Экзотические и узкоспециализированные методы

    • Сортировка Шелла — улучшенный вариант Insertion Sort с предварительными проходами
    • Гномья сортировка — любопытный алгоритм, похожий на Bubble Sort
    • Bogosort — теоретический алгоритм с O(n!) сложностью
    • Сортировка слиянием на месте — сложная модификация без дополнительной памяти
    • Сортировка с помощью красно-чёрных деревьев — демонстрационный алгоритм

    4. Критерии выбора алгоритма

    КритерийРекомендуемые алгоритмы
    Общий случайTimsort, QuickSort
    Ограниченная памятьHeapSort, In-place MergeSort
    Частично отсортированные данныеInsertionSort, Timsort
    Числовые данныеRadixSort, CountingSort
    Параллельная обработкаBitonicSort, SampleSort
    СтабильностьMergeSort, InsertionSort

    5. Современные тенденции

    Современные исследования в области сортировки сосредоточены на:

    • Алгоритмах для параллельных и распределённых систем
    • Адаптивных алгоритмах, учитывающих структуру данных
    • Гибридных подходах, автоматически выбирающих стратегию
    • Оптимизации для конкретных архитектур процессоров

    Большинство современных языков программирования используют сложные гибридные алгоритмы, которые автоматически подстраиваются под характер данных.

  • Быстрая сортировка (Quick Sort) на PHP и JavaScript

    Быстрая сортировка — один из самых эффективных алгоритмов сортировки с средней сложностью O(n log n). В этой статье разберём принцип работы и реализации на PHP и JavaScript.

    📌 Принцип работы Quick Sort

    • Выбор опорного элемента (pivot)
    • Разделение массива на две части: элементы меньше pivot и больше pivot
    • Рекурсивная сортировка обеих частей

    💻 Реализация Quick Sort на PHP

    <?php
    function quickSort(array $arr): array {
        if (count($arr) <= 1) {
            return $arr;
        }
        
        $pivot = $arr[0];
        $left = $right = [];
        
        for ($i = 1; $i < count($arr); $i++) {
            if ($arr[$i] < $pivot) {
                $left[] = $arr[$i];
            } else {
                $right[] = $arr[$i];
            }
        }
        
        return array_merge(quickSort($left), [$pivot], quickSort($right));
    }
    
    // Пример использования
    $array = [10, 80, 30, 90, 40, 50, 70];
    $sorted = quickSort($array);
    print_r($sorted);
    ?>

    🌐 Реализация Quick Sort на JavaScript

    1. Классическая реализация

    function quickSort(arr) {
        if (arr.length <= 1) return arr;
        
        const pivot = arr[0];
        const left = [];
        const right = [];
        
        for (let i = 1; i < arr.length; i++) {
            if (arr[i] < pivot) {
                left.push(arr[i]);
            } else {
                right.push(arr[i]);
            }
        }
        
        return [...quickSort(left), pivot, ...quickSort(right)];
    }
    
    // Пример использования
    const array = [10, 80, 30, 90, 40, 50, 70];
    console.log(quickSort(array));

    2. Оптимизированная версия (in-place)

    function quickSortInPlace(arr, left = 0, right = arr.length - 1) {
        if (left >= right) return;
        
        const pivot = partition(arr, left, right);
        quickSortInPlace(arr, left, pivot - 1);
        quickSortInPlace(arr, pivot + 1, right);
        
        return arr;
    }
    
    function partition(arr, left, right) {
        const pivot = arr[right];
        let i = left;
        
        for (let j = left; j < right; j++) {
            if (arr[j] < pivot) {
                [arr[i], arr[j]] = [arr[j], arr[i]];
                i++;
            }
        }
        
        [arr[i], arr[right]] = [arr[right], arr[i]];
        return i;
    }
    
    // Пример использования
    const nums = [10, 80, 30, 90, 40, 50, 70];
    console.log(quickSortInPlace([...nums]));

    ⚡ Сравнение Quick Sort с другими алгоритмами

    ХарактеристикаQuick SortMerge SortBubble Sort
    Средняя сложностьO(n log n)O(n log n)O(n²)
    Худший случайO(n²)O(n log n)O(n²)
    ПамятьO(log n)O(n)O(1)
    СтабильностьНетДаДа

    📌 Когда использовать Quick Sort?

    • Преимущества:
      • Обычно быстрее других алгоритмов на практике
      • Требует мало дополнительной памяти
      • Хорошо работает с кэшем процессора
    • Недостатки:
      • Худший случай O(n²) (редко при правильном выборе pivot)
      • Нестабильный

    Оптимальное применение: сортировка больших массивов в памяти, когда стабильность не важна.

    🔍 Дополнение: Нестабильность Quick Sort на практике

    Критически важное отличие Quick Sort от стабильных алгоритмов вроде Merge Sort — его нестабильность. Это означает, что при наличии одинаковых элементов их относительный порядок после сортировки может измениться.

    Наглядный пример

    // Данные: пользователи с одинаковым возрастом
    const users = [
      { name: 'Анна', age: 25 },
      { name: 'Иван', age: 30 },
      { name: 'Мария', age: 25 } // Такое же значение age, как у Анны
    ];
    
    // После Quick Sort возможен вариант:
    [
      { name: 'Мария', age: 25 }, // Мария теперь перед Анной!
      { name: 'Анна', age: 25 },
      { name: 'Иван', age: 30 }
    ]

    Когда это критично?

    • При сортировке таблиц по нескольким столбцам (например: сначала по дате, потом по имени)
    • В финансовых системах, где важен порядок операций с одинаковой суммой
    • При работе с хронологическими данными (новости, события)

    Как решить проблему?

    1. Добавить индекс сравнения:
      arr.map((item, index) => ({ ...item, _index: index }))
      // Сортировка с учётом _index при равенстве
    2. Использовать стабильные аналоги:
      • Merge Sort
      • Встроенный Array.prototype.sort() (стабилен в современных браузерах)
      • Timsort (Python, Java)
    СитуацияРекомендация
    Важна скорость + нет одинаковых ключейQuick Sort (оптимальный выбор)
    Работа с объектами/дублямиMerge Sort или встроенная сортировка
    Ограниченная памятьIn-place Quick Sort (но без стабильности)

    Это дополнение помогает понять, почему в некоторых случаях разработчики предпочитают Merge Sort, несмотря на его повышенные требования к памяти.

  • Сортировка слиянием (Merge Sort) на PHP и JavaScript

    Сортировка слиянием — это эффективный алгоритм сортировки, работающий по принципу «разделяй и властвуй». Он обладает стабильностью и гарантированной сложностью O(n log n).

    📌 Алгоритм сортировки слиянием

    • Разделение массива на две половины
    • Рекурсивная сортировка каждой половины
    • Слияние отсортированных половин

    💻 Реализация на PHP

    <?php
    function mergeSort(array $arr): array {
        if (count($arr) <= 1) {
            return $arr;
        }
        
        $middle = (int)(count($arr) / 2);
        $left = mergeSort(array_slice($arr, 0, $middle));
        $right = mergeSort(array_slice($arr, $middle));
        
        return merge($left, $right);
    }
    
    function merge(array $left, array $right): array {
        $result = [];
        $i = $j = 0;
        
        while ($i < count($left) && $j < count($right)) {
            if ($left[$i] < $right[$j]) {
                $result[] = $left[$i++];
            } else {
                $result[] = $right[$j++];
            }
        }
        
        return array_merge($result, array_slice($left, $i), array_slice($right, $j));
    }
    
    // Пример использования
    $array = [38, 27, 43, 3, 9, 82, 10];
    $sorted = mergeSort($array);
    print_r($sorted);
    ?>

    🌐 Реализация на JavaScript

    1. Классическая версия

    function mergeSort(arr) {
        if (arr.length <= 1) return arr;
        
        const middle = Math.floor(arr.length / 2);
        const left = mergeSort(arr.slice(0, middle));
        const right = mergeSort(arr.slice(middle));
        
        return merge(left, right);
    }
    
    function merge(left, right) {
        let result = [];
        let i = 0, j = 0;
        
        while (i < left.length && j < right.length) {
            if (left[i] < right[j]) {
                result.push(left[i++]);
            } else {
                result.push(right[j++]);
            }
        }
        
        return result.concat(left.slice(i)).concat(right.slice(j));
    }
    
    // Пример использования
    const array = [38, 27, 43, 3, 9, 82, 10];
    console.log(mergeSort(array));

    2. Версия с визуализацией шагов

    function mergeSortWithSteps(arr, level = 0) {
        console.log(`${'  '.repeat(level)}Сортируем:`, arr);
        
        if (arr.length <= 1) {
            console.log(`${'  '.repeat(level)}Базовый случай:`, arr);
            return arr;
        }
        
        const middle = Math.floor(arr.length / 2);
        const left = mergeSortWithSteps(arr.slice(0, middle), level + 1);
        const right = mergeSortWithSteps(arr.slice(middle), level + 1);
        
        const merged = merge(left, right);
        console.log(`${'  '.repeat(level)}Сливаем ${left} и ${right} →`, merged);
        
        return merged;
    }
    
    const nums = [5, 2, 4, 7, 1];
    console.log("Исходный массив:", nums);
    console.log("Результат:", mergeSortWithSteps(nums));

    ⚡ Сравнение с другими алгоритмами

    ХарактеристикаMerge SortQuick SortBubble Sort
    Сложность (средняя)O(n log n)O(n log n)O(n²)
    Сложность (худшая)O(n log n)O(n²)O(n²)
    ПамятьO(n)O(log n)O(1)
    СтабильностьДаНетДа

    📌 Когда использовать?

    • Плюсы Merge Sort:
      • Гарантированная сложность O(n log n)
      • Стабильность (сохраняет порядок равных элементов)
      • Хорош для связных списков и внешней сортировки
    • Минусы:
      • Требует дополнительной памяти O(n)
      • Медленнее Quick Sort на небольших массивах

    Оптимальное применение: большие массивы (от 10k элементов), где важна стабильность.

    Другие виды сортировок

    • Quick Sort — быстрая сортировка
    • Bubble Sort — сортировка пузырьком
  • Сортировка пузырьком в PHP и JavaScript: примеры и объяснение

    Сортировка пузырьком (Bubble Sort) — один из самых простых алгоритмов сортировки, который отлично подходит для обучения основам алгоритмов. В этой статье мы разберём:

    • Как работает сортировка пузырьком
    • Реализацию на PHP 8+ и JavaScript (ES6+)
    • Оптимизацию алгоритма
    • Примеры с пошаговым выводом

    📌 Как работает сортировка пузырьком?

    Алгоритм последовательно сравнивает соседние элементы массива и меняет их местами, если они расположены в неправильном порядке. Этот процесс повторяется до тех пор, пока массив не будет полностью отсортирован.

    Основные шаги:

    • Проход по массиву от начала до конца.
    • Сравнение текущего элемента (arr[j]) со следующим (arr[j+1]).
    • Если arr[j] > arr[j+1], меняем их местами.
    • Повторяем, пока весь массив не станет упорядоченным.

    🔹 Временная сложность:

    • Худший случай: O(n²) (если массив отсортирован в обратном порядке).
    • Лучший случай: O(n) (если массив уже отсортирован и используется оптимизация).

    💻 Реализация на PHP (версия 8.0+)

    <?php
    function bubbleSort(array $arr): array {
        $n = count($arr);
        for ($i = 0; $i < $n; $i++) {
            $swapped = false; // Флаг для оптимизации
            for ($j = 0; $j < $n - $i - 1; $j++) {
                if ($arr[$j] > $arr[$j + 1]) {
                    // Обмен значений (деструктуризация в PHP 7.1+)
                    [$arr[$j], $arr[$j + 1]] = [$arr[$j + 1], $arr[$j]];
                    $swapped = true;
                }
            }
            // Если обменов не было, массив уже отсортирован
            if (!$swapped) break;
        }
        return $arr;
    }
    
    // Пример использования
    $array = [64, 34, 25, 12, 22, 11, 90];
    $sortedArray = bubbleSort($array);
    echo "Отсортированный массив: " . implode(", ", $sortedArray);
    ?>

    🔸 Пояснение:

    • Используется деструктуризация ([$a, $b] = [$b, $a]), доступная с PHP 7.1+.
    • Флаг $swapped позволяет досрочно завершить сортировку, если массив уже упорядочен.

    Вывод:

    Отсортированный массив: 11, 12, 22, 25, 34, 64, 90

    🌐 Реализация на JavaScript (ES6+)

    1. Базовая версия

    function bubbleSort(arr) {
        const n = arr.length;
        for (let i = 0; i < n; i++) {
            let swapped = false;
            for (let j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // Обмен значений (деструктуризация в ES6)
                    [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
                    swapped = true;
                }
            }
            if (!swapped) break;
        }
        return arr;
    }
    
    const array = [64, 34, 25, 12, 22, 11, 90];
    console.log("Отсортированный массив:", bubbleSort(array));

    Вывод:

    [11, 12, 22, 25, 34, 64, 90]

    2. Версия с пошаговым выводом (для обучения)

    function bubbleSortWithSteps(arr) {
        const n = arr.length;
        for (let i = 0; i < n; i++) {
            let swapped = false;
            console.log(`🔹 Проход ${i + 1}:`);
            for (let j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
                    swapped = true;
                    console.log(`   Поменяли ${arr[j]} ↔ ${arr[j + 1]}:`, [...arr]);
                }
            }
            if (!swapped) break;
        }
        return arr;
    }
    
    const nums = [5, 3, 8, 4, 2];
    console.log("Исходный массив:", nums);
    bubbleSortWithSteps(nums);
    console.log("Результат:", nums);

    Вывод:

    Исходный массив: [5, 3, 8, 4, 2]
    🔹 Проход 1:
       Поменяли 3 ↔ 5: [3, 5, 8, 4, 2]
       Поменяли 4 ↔ 8: [3, 5, 4, 8, 2]
       Поменяли 2 ↔ 8: [3, 5, 4, 2, 8]
    🔹 Проход 2:
       Поменяли 4 ↔ 5: [3, 4, 5, 2, 8]
       Поменяли 2 ↔ 5: [3, 4, 2, 5, 8]
    🔹 Проход 3:
       Поменяли 2 ↔ 4: [3, 2, 4, 5, 8]
    🔹 Проход 4:
       Поменяли 2 ↔ 3: [2, 3, 4, 5, 8]
    Результат: [2, 3, 4, 5, 8]

    📌 Итог

    • PHP 8+ и JavaScript (ES6+) поддерживают современный синтаксис (деструктуризацию).
    • Оптимизация с флагом swapped ускоряет сортировку в лучшем случае до O(n).
    • Сложность в худшем случае — O(n²), поэтому для больших массивов лучше использовать быструю сортировку QuickSort, сортировку слиянием MergeSort или встроенные методы (sort()).

    🚀 Где применять?

    • Для обучения алгоритмам.
    • Для небольших массивов (до 1000 элементов).

    📢 Ваше мнение:

    Пользуетесь ли вы сортировкой пузырьком в реальных проектах? Делитесь в комментариях!

  • Анатомия zval: Основные компоненты (PHP 7+)

    В этой статье мы детально разберём структуру zval в современных версиях PHP, начиная с революционного PHP 7. Вы узнаете, как организовано хранение переменных на низком уровне и какие оптимизации были внедрены.

    2.1. Основные компоненты zval

    Структура _zval_struct

    struct _zval_struct {
        zend_value value;    // Основное значение (64 бита)
        union {
            struct {
                zend_uchar type;         // Тип данных (IS_LONG, IS_STRING и др.)
                zend_uchar type_flags;    // Флаги поведения типа
                zend_uchar const_flags;   // Флаги константности
                zend_uchar reserved;      // Зарезервировано
            } v;
            uint32_t type_info;           // Альтернативное представление
        } u1;                            // 32 бита
        union u2 {                       // 32 бита (служебные данные)
            uint32_t next;               // Для управления хеш-таблицами
            uint32_t cache_slot;         // Кеширование
            uint32_t lineno;             // Номер строки (для ошибок)
            uint32_t num_args;           // Количество аргументов
            uint32_t fe_pos;             // Позиция в foreach
            // ... другие служебные поля
        };
    }; // Всего 16 байт

    1. Компонент value (zend_value)

    ТипПолеРазмерОписание
    Целоеlval64 битаДля типов IS_LONG, IS_BOOL
    Дробноеdval64 битаДля IS_DOUBLE
    Указательstr64 битаДля строк (IS_STRING)
    Указательarr64 битаДля массивов (IS_ARRAY)

    2. Компонент u1 (метаданные типа)

    • type — определяет базовый тип данных (IS_LONG, IS_STRING и др.)
    • type_flags — дополнительные флаги поведения:
      • IS_TYPE_REFCOUNTED (требует подсчета ссылок)
      • IS_TYPE_COPYABLE (поддерживает Copy-On-Write)
      • IS_TYPE_IMMUTABLE (неизменяемый тип)
    • const_flags — флаги константности

    3. Компонент u2 (служебные данные)

    Этот компонент используется для различных оптимизаций:

    • next — связь элементов в хеш-таблицах
    • cache_slot — кеширование для быстрого доступа
    • lineno — отладка (номер строки в исходном коде)
    • num_args — информация о вызовах функций

    Практический пример

    // Создание zval с целым числом
    zval my_zval;
    ZVAL_LONG(&my_zval, 42);
    
    // Доступ к данным
    if (Z_TYPE(my_zval) == IS_LONG) {
        zend_long value = Z_LVAL(my_zval); // 42
        php_printf("Значение: %ld\n", value);
    }

    Продолжение следует в следующей статье цикла…

  • Введение в устройство zval: фундамент обработки данных в PHP

    Zval (Zend value) — фундаментальная структура данных в ядре PHP, ответственная за хранение и обработку всех переменных. В этой статье мы исследуем её эволюцию, современную реализацию и практическое значение для разработчиков.

    Историческая эволюция zval

    PHP 5: Первое поколение

    • 24-байтовая структура
    • Универсальный подсчет ссылок
    • Высокие накладные расходы

    PHP 7: Революция

    • 16 байт (экономия 33%)
    • Разделение value/type
    • Оптимизация скаляров

    PHP 8: Совершенство

    • Дополнительные оптимизации
    • Интеграция с JIT
    • Улучшенные хеш-таблицы

    Структура zval в PHP 8

    struct _zval_struct {
        zend_value value;    // 64-битное значение
        union {
            struct {
                zend_uchar type;        // Тип данных
                zend_uchar type_flags;  // Флаги поведения
                zend_uchar const_flags; // Константность
                zend_uchar reserved;    // Выравнивание
            } v;
            uint32_t type_info;
        } u1;
        union u2 {
            uint32_t next;
            // ... служебные поля
        };
    }; // Всего 16 байт

    Ключевые оптимизации

    • Отказ от refcount для скаляров — целые числа и булевы значения больше не используют подсчет ссылок
    • Copy-On-Write — сложные структуры копируются только при модификации
    • Встроенное кеширование хешей — ускорение операций сравнения строк
    • Оптимизированные хеш-таблицы — быстрый доступ к элементам массивов

    Практическое применение для разработчиков и авторов расширений

    • Оптимизация потребления памяти
    • Эффективная работа с переменными
    • Понимание поведения типов

    Пример:

    // Создание строки в расширении
    zend_string *str = zend_string_init("test", 4, 0);
    zval zv;
    ZVAL_STR(&zv, str);